on xenial help sign in
Add new right-edge screen, not fully finished yet
a4e9166Michael Terry
qml/Shell.qml+2−2
@@ -297,12 +297,12 @@ Item {
297 Binding {
297 Binding {
298 target: applicationsDisplayLoader.item
298 target: applicationsDisplayLoader.item
299 property: "interactive"
299 property: "interactive"
300− value: tutorial.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
300+ value: tutorial.spreadEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
301 }
301 }
302 Binding {
302 Binding {
303 target: applicationsDisplayLoader.item
303 target: applicationsDisplayLoader.item
304 property: "spreadEnabled"
304 property: "spreadEnabled"
305− value: tutorial.stagesEnabled && !greeter.hasLockedApp
305+ value: tutorial.spreadEnabled && !greeter.hasLockedApp
306 }
306 }
307 Binding {
307 Binding {
308 target: applicationsDisplayLoader.item
308 target: applicationsDisplayLoader.item
qml/Stages/ApplicationWindow.qml+14−1
@@ -43,6 +43,7 @@ Item {
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
43 readonly property color splashColor: root.application ? root.application.splashColor : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
44 readonly property color splashColorHeader: root.application ? root.application.splashColorHeader : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
45 readonly property color splashColorFooter: root.application ? root.application.splashColorFooter : "#00000000"
46+ readonly property url defaultScreenshot: root.application ? root.application.defaultScreenshot : ""
46
47
47 // Whether the Application had a surface before but lost it.
48 // Whether the Application had a surface before but lost it.
48 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
49 property bool hadSurface: sessionContainer.surfaceContainer.hadSurface
@@ -72,7 +73,7 @@ Item {
72 Image {
73 Image {
73 id: screenshotImage
74 id: screenshotImage
74 objectName: "screenshotImage"
75 objectName: "screenshotImage"
75− source: ""
76+ source: d.defaultScreenshot
76 anchors.fill: parent
77 anchors.fill: parent
77 antialiasing: !root.interactive
78 antialiasing: !root.interactive
78
79
@@ -123,6 +124,7 @@ Item {
123 }
124 }
124
125
125 StateGroup {
126 StateGroup {
127+ id: stateGroup
126 objectName: "applicationWindowStateGroup"
128 objectName: "applicationWindowStateGroup"
127 states: [
129 states: [
128 State {
130 State {
@@ -215,6 +217,17 @@ Item {
215 PropertyAction { target: screenshotImage; property: "source"; value: "" }
217 PropertyAction { target: screenshotImage; property: "source"; value: "" }
216 }
218 }
217 },
219 },
220+ Transition {
221+ from: "splashScreen"; to: "screenshot"
222+ SequentialAnimation {
223+ PropertyAction { target: screenshotImage
224+ property: "visible"; value: true }
225+ UbuntuNumberAnimation { target: screenshotImage; property: "opacity";
226+ from: 0.0; to: 1.0
227+ duration: UbuntuAnimation.BriskDuration }
228+ PropertyAction { target: splashLoader; property: "active"; value: false }
229+ }
230+ },
218 Transition {
231 Transition {
219 from: "surface"; to: "void"
232 from: "surface"; to: "void"
220 SequentialAnimation {
233 SequentialAnimation {
qml/Stages/PhoneStage.qml+54−31
@@ -32,6 +32,18 @@ Rectangle {
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
32 property bool spreadEnabled: true // If false, animations and right edge will be disabled
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
33 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
34 property int orientation: Qt.PortraitOrientation
34 property int orientation: Qt.PortraitOrientation
35+ property QtObject applicationManager: ApplicationManager
36+ property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
37+ property bool altTabEnabled: true
38+ property real startScale: 1.1
39+ property real endScale: 0.7
40+
41+ // How far left the stage has been dragged
42+ readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
43+
44+ readonly property alias dragging: spreadDragArea.dragging
45+
46+ signal opened()
35
47
36 color: "#111111"
48 color: "#111111"
37
49
@@ -52,30 +64,30 @@ Rectangle {
52 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
64 if (inverseProgress == 0 && priv.oldInverseProgress > 0) {
53 // left edge drag released. Minimum distance is given by design.
65 // left edge drag released. Minimum distance is given by design.
54 if (priv.oldInverseProgress > units.gu(22)) {
66 if (priv.oldInverseProgress > units.gu(22)) {
55− ApplicationManager.focusApplication("unity8-dash");
67+ applicationManager.focusApplication("unity8-dash");
56 }
68 }
57 }
69 }
58 priv.oldInverseProgress = inverseProgress;
70 priv.oldInverseProgress = inverseProgress;
59 }
71 }
60
72
61 Connections {
73 Connections {
62− target: ApplicationManager
74+ target: applicationManager
63
75
64 onFocusRequested: {
76 onFocusRequested: {
65 if (spreadView.phase > 0) {
77 if (spreadView.phase > 0) {
66 spreadView.snapTo(priv.indexOf(appId));
78 spreadView.snapTo(priv.indexOf(appId));
67 } else {
79 } else {
68− ApplicationManager.focusApplication(appId);
80+ applicationManager.focusApplication(appId);
69 }
81 }
70 }
82 }
71
83
72 onApplicationAdded: {
84 onApplicationAdded: {
73 if (spreadView.phase == 2) {
85 if (spreadView.phase == 2) {
74− spreadView.snapTo(ApplicationManager.count - 1);
86+ spreadView.snapTo(applicationManager.count - 1);
75 } else {
87 } else {
76 spreadView.phase = 0;
88 spreadView.phase = 0;
77 spreadView.contentX = -spreadView.shift;
89 spreadView.contentX = -spreadView.shift;
78− ApplicationManager.focusApplication(appId);
90+ applicationManager.focusApplication(appId);
79 }
91 }
80 }
92 }
81
93
@@ -90,9 +102,9 @@ Rectangle {
90 }
102 }
91
103
92 function focusTopMostApp() {
104 function focusTopMostApp() {
93− if (ApplicationManager.count > 0) {
105+ if (applicationManager.count > 0) {
94− var topmostApp = ApplicationManager.get(0);
106+ var topmostApp = applicationManager.get(0);
95− ApplicationManager.focusApplication(topmostApp.appId);
107+ applicationManager.focusApplication(topmostApp.appId);
96 }
108 }
97 }
109 }
98 }
110 }
@@ -100,8 +112,9 @@ Rectangle {
100 QtObject {
112 QtObject {
101 id: priv
113 id: priv
102
114
115+ readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
103− property string focusedAppId: ApplicationManager.focusedApplicationId
116+ property string focusedAppId: applicationManager.focusedApplicationId
104− property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
117+ property var focusedApplication: applicationManager.findApplication(focusedAppId)
105 property var focusedAppDelegate: null
118 property var focusedAppDelegate: null
106
119
107 property real oldInverseProgress: 0
120 property real oldInverseProgress: 0
@@ -110,8 +123,8 @@ Rectangle {
110 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
123 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
111
124
112 function indexOf(appId) {
125 function indexOf(appId) {
113− for (var i = 0; i < ApplicationManager.count; i++) {
126+ for (var i = 0; i < applicationManager.count; i++) {
114− if (ApplicationManager.get(i).appId == appId) {
127+ if (applicationManager.get(i).appId == appId) {
115 return i;
128 return i;
116 }
129 }
117 }
130 }
@@ -197,10 +210,17 @@ Rectangle {
197 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
210 // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
198 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
211 snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
199 snapAnimation.start();
212 snapAnimation.start();
213+ root.opened();
200 }
214 }
201 }
215 }
202 function snapTo(index) {
216 function snapTo(index) {
203− if (ApplicationManager.count <= index) {
217+ if (!root.altTabEnabled) {
218+ // Reset to start instead
219+ snapAnimation.targetContentX = -shift;
220+ snapAnimation.start();
221+ return;
222+ }
223+ if (applicationManager.count <= index) {
204 // In case we're trying to snap to some non existing app, lets snap back to the first one
224 // In case we're trying to snap to some non existing app, lets snap back to the first one
205 index = 0;
225 index = 0;
206 }
226 }
@@ -215,7 +235,7 @@ Rectangle {
215 snapAnimation.start();
235 snapAnimation.start();
216 }
236 }
217
237
218− // In case the ApplicationManager already holds an app when starting up we're missing animations
238+ // In case the applicationManager already holds an app when starting up we're missing animations
219 // Make sure we end up in the same state
239 // Make sure we end up in the same state
220 Component.onCompleted: {
240 Component.onCompleted: {
221 spreadView.contentX = -spreadView.shift
241 spreadView.contentX = -spreadView.shift
@@ -235,7 +255,7 @@ Rectangle {
235 ScriptAction {
255 ScriptAction {
236 script: {
256 script: {
237 if (spreadView.selectedIndex >= 0) {
257 if (spreadView.selectedIndex >= 0) {
238− ApplicationManager.focusApplication(ApplicationManager.get(spreadView.selectedIndex).appId);
258+ applicationManager.focusApplication(applicationManager.get(spreadView.selectedIndex).appId);
239
259
240 spreadView.selectedIndex = -1;
260 spreadView.selectedIndex = -1;
241 spreadView.phase = 0;
261 spreadView.phase = 0;
@@ -250,7 +270,7 @@ Rectangle {
250 // This width controls how much the spread can be flicked left/right. It's composed of:
270 // This width controls how much the spread can be flicked left/right. It's composed of:
251 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
271 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
252 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
272 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
253− width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
273+ width: Math.max(3, applicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
254 height: parent.height
274 height: parent.height
255 Behavior on width {
275 Behavior on width {
256 enabled: spreadView.closingIndex >= 0
276 enabled: spreadView.closingIndex >= 0
@@ -265,20 +285,22 @@ Rectangle {
265 x: spreadView.contentX
285 x: spreadView.contentX
266
286
267 onClicked: {
287 onClicked: {
288+ if (root.altTabEnabled) {
268− spreadView.snapTo(0);
289+ spreadView.snapTo(0);
290+ }
269 }
291 }
270
292
271 Repeater {
293 Repeater {
272 id: spreadRepeater
294 id: spreadRepeater
273 objectName: "spreadRepeater"
295 objectName: "spreadRepeater"
274− model: ApplicationManager
296+ model: applicationManager
275 delegate: TransformedSpreadDelegate {
297 delegate: TransformedSpreadDelegate {
276 id: appDelegate
298 id: appDelegate
277 objectName: "appDelegate" + index
299 objectName: "appDelegate" + index
278 startAngle: 45
300 startAngle: 45
279 endAngle: 5
301 endAngle: 5
280− startScale: 1.1
302+ startScale: root.startScale
281− endScale: 0.7
303+ endScale: root.endScale
282 startDistance: spreadView.tileDistance
304 startDistance: spreadView.tileDistance
283 endDistance: units.gu(.5)
305 endDistance: units.gu(.5)
284 width: spreadView.width
306 width: spreadView.width
@@ -286,11 +308,12 @@ Rectangle {
286 selected: spreadView.selectedIndex == index
308 selected: spreadView.selectedIndex == index
287 otherSelected: spreadView.selectedIndex >= 0 && !selected
309 otherSelected: spreadView.selectedIndex >= 0 && !selected
288 interactive: !spreadView.interactive && spreadView.phase === 0
310 interactive: !spreadView.interactive && spreadView.phase === 0
289− && spreadView.shiftedContentX === 0 && root.interactive && index === 0
311+ && spreadView.shiftedContentX === 0 && root.interactive && isFocused
290− swipeToCloseEnabled: spreadView.interactive && !snapAnimation.running
312+ swipeToCloseEnabled: spreadView.interactive && root.interactive && !snapAnimation.running
291 maximizedAppTopMargin: root.maximizedAppTopMargin
313 maximizedAppTopMargin: root.maximizedAppTopMargin
292 dropShadow: spreadView.active ||
314 dropShadow: spreadView.active ||
293 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
315 (priv.focusedAppDelegate && priv.focusedAppDelegate.x !== 0)
316+ focusFirstApp: root.focusFirstApp
294
317
295 readonly property bool isDash: model.appId == "unity8-dash"
318 readonly property bool isDash: model.appId == "unity8-dash"
296
319
@@ -298,7 +321,7 @@ Rectangle {
298
321
299 x: {
322 x: {
300 // focused app is always positioned at 0 except when following left edge drag
323 // focused app is always positioned at 0 except when following left edge drag
301− if (index == 0) {
324+ if (isFocused) {
302 if (!isDash && root.inverseProgress > 0) {
325 if (!isDash && root.inverseProgress > 0) {
303 return root.inverseProgress;
326 return root.inverseProgress;
304 }
327 }
@@ -309,10 +332,10 @@ Rectangle {
309 }
332 }
310
333
311 // Otherwise line up for the spread
334 // Otherwise line up for the spread
312− return spreadView.width + (index - 1) * spreadView.tileDistance;
335+ return spreadView.width + spreadIndex * spreadView.tileDistance;
313 }
336 }
314
337
315− application: ApplicationManager.get(index)
338+ application: applicationManager.get(index)
316 closeable: !isDash
339 closeable: !isDash
317
340
318 property real behavioredIndex: index
341 property real behavioredIndex: index
@@ -350,7 +373,7 @@ Rectangle {
350 progress: {
373 progress: {
351 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
374 var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
352 // Tile 1 needs to move directly from the beginning...
375 // Tile 1 needs to move directly from the beginning...
353− if (behavioredIndex == 1 && spreadView.phase < 2) {
376+ if (root.focusFirstApp && behavioredIndex == 1 && spreadView.phase < 2) {
354 tileProgress += spreadView.tileDistance / spreadView.width;
377 tileProgress += spreadView.tileDistance / spreadView.width;
355 }
378 }
356 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
379 // Limiting progress to ~0 and 1.7 to avoid binding calculations when tiles are not
@@ -365,7 +388,7 @@ Rectangle {
365
388
366 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
389 // This mostly is the same as progress, just adds the snapping to phase 1 for tiles 0 and 1
367 animatedProgress: {
390 animatedProgress: {
368− if (spreadView.phase == 0 && index < 2) {
391+ if (spreadView.phase == 0 && index <= priv.firstSpreadIndex) {
369 if (progress < spreadView.positionMarker1) {
392 if (progress < spreadView.positionMarker1) {
370 return progress;
393 return progress;
371 } else if (progress < spreadView.positionMarker1 + 0.05){
394 } else if (progress < spreadView.positionMarker1 + 0.05){
@@ -390,11 +413,11 @@ Rectangle {
390 }
413 }
391
414
392 onClicked: {
415 onClicked: {
393− if (spreadView.phase == 2) {
416+ if (root.altTabEnabled && spreadView.phase == 2) {
394− if (ApplicationManager.focusedApplicationId == ApplicationManager.get(index).appId) {
417+ if (applicationManager.focusedApplicationId == applicationManager.get(index).appId) {
395 spreadView.snapTo(index);
418 spreadView.snapTo(index);
396 } else {
419 } else {
397− ApplicationManager.requestFocusApplication(ApplicationManager.get(index).appId);
420+ applicationManager.requestFocusApplication(applicationManager.get(index).appId);
398 }
421 }
399 }
422 }
400 }
423 }
@@ -409,7 +432,7 @@ Rectangle {
409
432
410 onClosed: {
433 onClosed: {
411 spreadView.closingIndex = index;
434 spreadView.closingIndex = index;
412− ApplicationManager.stopApplication(ApplicationManager.get(index).appId);
435+ applicationManager.stopApplication(applicationManager.get(index).appId);
413 }
436 }
414 }
437 }
415 }
438 }
qml/Stages/TransformedSpreadDelegate.qml+27−21
@@ -46,6 +46,18 @@ SpreadDelegate {
46 property real startDistance: units.gu(5)
46 property real startDistance: units.gu(5)
47 property real endDistance: units.gu(.5)
47 property real endDistance: units.gu(.5)
48
48
49+ // Whether first app is displayed entirely vs just on the spread edge
50+ property bool focusFirstApp: true
51+
52+ // Whether this delegate is displayed to the user outside of the spread
53+ readonly property bool isFocused: focusFirstApp && index === 0
54+
55+ // Adjusted index for any spread animation calculations
56+ readonly property int spreadIndex: index - (focusFirstApp ? 1 : 0)
57+
58+ // How far left this delegate has been translated
59+ readonly property alias xTranslate: priv.xTranslate
60+
49 onSelectedChanged: {
61 onSelectedChanged: {
50 if (selected) {
62 if (selected) {
51 priv.snapshot();
63 priv.snapshot();
@@ -63,8 +75,8 @@ SpreadDelegate {
63 Connections {
75 Connections {
64 target: spreadView
76 target: spreadView
65 onPhaseChanged: {
77 onPhaseChanged: {
66− if (spreadView.phase == 1) {
78+ if (root.focusFirstApp && spreadView.phase == 1) {
67− if (index == 0) {
79+ if (index === 0) {
68 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
80 priv.phase2startTranslate = priv.easingAnimation(0, spreadView.positionMarker4, 0, -spreadView.width, spreadView.positionMarker4) + spreadView.width;
69 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
81 priv.phase2startAngle = priv.easingAnimation(0, spreadView.positionMarker4, root.startAngle, root.endAngle, spreadView.positionMarker4);
70 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
82 priv.phase2startScale = priv.easingAnimation(0, spreadView.positionMarker4, root.startScale, root.endScale, spreadView.positionMarker4);
@@ -134,7 +146,7 @@ SpreadDelegate {
134 }
146 }
135
147
136 if (otherSelected) {
148 if (otherSelected) {
137− if (spreadView.phase < 2 && index == 0) {
149+ if (spreadView.phase < 2 && root.isFocused) {
138 return linearAnimation(selectedProgress, 0, selectedXTranslate,
150 return linearAnimation(selectedProgress, 0, selectedXTranslate,
139 selectedXTranslate - spreadView.tileDistance, root.progress);
151 selectedXTranslate - spreadView.tileDistance, root.progress);
140 }
152 }
@@ -142,8 +154,7 @@ SpreadDelegate {
142 return selectedXTranslate;
154 return selectedXTranslate;
143 }
155 }
144
156
145− switch (index) {
157+ if (root.focusFirstApp && index === 0) {
146− case 0:
147 if (spreadView.phase == 0) {
158 if (spreadView.phase == 0) {
148 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
159 return Math.min(0, linearAnimation(0, spreadView.positionMarker2,
149 0, -spreadView.width * .25, root.animatedProgress));
160 0, -spreadView.width * .25, root.animatedProgress));
@@ -153,11 +164,10 @@ SpreadDelegate {
153 } else if (!priv.isSelected){ // phase 2
164 } else if (!priv.isSelected){ // phase 2
154 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
165 // Apply the same animation as with the rest but add spreadView.width to align it with the others.
155 return -easingCurve.value * spreadView.width + spreadView.width;
166 return -easingCurve.value * spreadView.width + spreadView.width;
156− } else if (priv.isSelected) {
167+ } else {
157 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
168 return linearAnimation(selectedProgress, 0, selectedXTranslate, 0, root.progress);
158 }
169 }
159−
170+ } else if (root.focusFirstApp && index === 1) {
160− case 1:
161 if (spreadView.phase == 0 && !priv.isSelected) {
171 if (spreadView.phase == 0 && !priv.isSelected) {
162 return linearAnimation(0, spreadView.positionMarker2,
172 return linearAnimation(0, spreadView.positionMarker2,
163 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
173 0, -spreadView.width * spreadView.snapPosition, root.animatedProgress);
@@ -170,13 +180,13 @@ SpreadDelegate {
170
180
171 if (priv.isSelected) {
181 if (priv.isSelected) {
172 // Distance to left edge
182 // Distance to left edge
173− var targetTranslate = -spreadView.width - ((index - 1) * root.startDistance);
183+ var targetTranslate = -spreadView.width - (root.spreadIndex * root.startDistance);
174 return linearAnimation(selectedProgress, 0,
184 return linearAnimation(selectedProgress, 0,
175 selectedXTranslate, targetTranslate, root.progress);
185 selectedXTranslate, targetTranslate, root.progress);
176 }
186 }
177
187
178 // Fix it at the right edge...
188 // Fix it at the right edge...
179− var rightEdgeOffset = -((index - 1) * root.startDistance);
189+ var rightEdgeOffset = -(root.spreadIndex * root.startDistance);
180 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
190 // ...and use our easing to move them to the left. Stop a bit earlier for each tile
181 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
191 var animatedEndDistance = linearAnimation(0, 2, root.endDistance, 0, root.progress);
182 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
192 return -easingCurve.value * spreadView.width + (index * animatedEndDistance) + rightEdgeOffset;
@@ -194,8 +204,7 @@ SpreadDelegate {
194 if (priv.isSelected) {
204 if (priv.isSelected) {
195 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
205 return linearAnimation(selectedProgress, 0, selectedAngle, 0, root.progress);
196 }
206 }
197− switch (index) {
207+ if (root.focusFirstApp && index === 0) {
198− case 0:
199 if (spreadView.phase == 0) {
208 if (spreadView.phase == 0) {
200 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
209 return Math.max(0, linearAnimation(0, spreadView.positionMarker2,
201 0, root.tile0SnapAngle, root.animatedProgress));
210 0, root.tile0SnapAngle, root.animatedProgress));
@@ -203,7 +212,7 @@ SpreadDelegate {
203 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
212 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
204 root.tile0SnapAngle, phase2startAngle, root.progress);
213 root.tile0SnapAngle, phase2startAngle, root.progress);
205 }
214 }
206− case 1:
215+ } else if (root.focusFirstApp && index === 1) {
207 if (spreadView.phase == 0) {
216 if (spreadView.phase == 0) {
208 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
217 return linearAnimation(0, spreadView.positionMarker2, root.startAngle,
209 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
218 root.startAngle * (1-spreadView.snapPosition), root.animatedProgress);
@@ -227,15 +236,14 @@ SpreadDelegate {
227 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
236 return linearAnimation(selectedProgress, 0, selectedScale, 1, root.progress);
228 }
237 }
229
238
230− switch (index) {
239+ if (root.focusFirstApp && index === 0) {
231− case 0:
232 if (spreadView.phase == 0) {
240 if (spreadView.phase == 0) {
233 return 1;
241 return 1;
234 } else if (spreadView.phase == 1) {
242 } else if (spreadView.phase == 1) {
235 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
243 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
236 1, phase2startScale, root.progress);
244 1, phase2startScale, root.progress);
237 }
245 }
238− case 1:
246+ } else if (root.focusFirstApp && index === 1) {
239 if (spreadView.phase == 0) {
247 if (spreadView.phase == 0) {
240 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
248 var targetScale = tile1StartScale - ((tile1StartScale - 1) * spreadView.snapPosition);
241 return linearAnimation(0, spreadView.positionMarker2,
249 return linearAnimation(0, spreadView.positionMarker2,
@@ -254,7 +262,7 @@ SpreadDelegate {
254 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
262 return linearAnimation (selectedProgress, Math.max(0, selectedProgress - .5),
255 selectedOpacity, 0, root.progress);
263 selectedOpacity, 0, root.progress);
256 }
264 }
257− if (index == 0) {
265+ if (root.isFocused) {
258 switch (spreadView.phase) {
266 switch (spreadView.phase) {
259 case 0:
267 case 0:
260 return linearAnimation(0, spreadView.positionMarker2, 1, .7, root.animatedProgress);
268 return linearAnimation(0, spreadView.positionMarker2, 1, .7, root.animatedProgress);
@@ -272,16 +280,14 @@ SpreadDelegate {
272 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
280 return linearAnimation(selectedProgress, 0, selectedTopMarginProgress, 0, root.progress);
273 }
281 }
274
282
275− switch (index) {
283+ if (root.focusFirstApp && index === 0) {
276− case 0:
277 if (spreadView.phase == 0) {
284 if (spreadView.phase == 0) {
278 return 0;
285 return 0;
279 } else if (spreadView.phase == 1) {
286 } else if (spreadView.phase == 1) {
280 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
287 return linearAnimation(spreadView.positionMarker2, spreadView.positionMarker4,
281 0, priv.phase2startTopMarginProgress, root.progress);
288 0, priv.phase2startTopMarginProgress, root.progress);
282 }
289 }
283− break;
290+ } else if (root.focusFirstApp && index === 1) {
284− case 1:
285 if (spreadView.phase == 0) {
291 if (spreadView.phase == 0) {
286 return 0;
292 return 0;
287 } else if (spreadView.phase == 1) {
293 } else if (spreadView.phase == 1) {
qml/Tutorial/Tick.qmlnew file+29
@@ -0,0 +1,29 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+
20+MouseArea {
21+ implicitHeight: tick.height
22+ implicitWidth: tick.width
23+ Image {
24+ id: tick
25+ source: Qt.resolvedUrl("graphics/tick.png")
26+ height: units.gu(6.5)
27+ width: units.gu(6.5)
28+ }
29+}
qml/Tutorial/Tutorial.qml+1−1
@@ -30,7 +30,7 @@ Item {
30 property Item overlay
30 property Item overlay
31
31
32 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
32 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
33− readonly property bool stagesEnabled: loader.item ? loader.item.stagesEnabled : true
33+ readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
34 readonly property bool panelEnabled: loader.item ? loader.item.panelEnabled : true
34 readonly property bool panelEnabled: loader.item ? loader.item.panelEnabled : true
35 readonly property bool panelContentEnabled: loader.item ? loader.item.panelContentEnabled : true
35 readonly property bool panelContentEnabled: loader.item ? loader.item.panelContentEnabled : true
36 readonly property bool running: loader.item ? loader.item.running : false
36 readonly property bool running: loader.item ? loader.item.running : false
qml/Tutorial/TutorialBottomFinish.qml+3−9
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19+import "." as LocalComponents
19
20
20TutorialPage {
21TutorialPage {
21 id: root
22 id: root
@@ -26,21 +27,14 @@ TutorialPage {
26
27
27 foreground {
28 foreground {
28 children: [
29 children: [
29− Image {
30+ LocalComponents.Tick {
30 objectName: "tick"
31 objectName: "tick"
31 anchors {
32 anchors {
32 horizontalCenter: parent.horizontalCenter
33 horizontalCenter: parent.horizontalCenter
33 top: parent.top
34 top: parent.top
34 topMargin: root.textBottom + units.gu(3)
35 topMargin: root.textBottom + units.gu(3)
35 }
36 }
36− source: Qt.resolvedUrl("graphics/tick.png")
37− height: units.gu(6.5)
38− width: units.gu(6.5)
39−
40− MouseArea {
41− anchors.fill: parent
42− onClicked: root.hide()
37+ onClicked: root.hide()
43− }
44 }
38 }
45 ]
39 ]
46 }
40 }
qml/Tutorial/TutorialContent.qml+15−2
@@ -27,7 +27,7 @@ Item {
27
27
28 readonly property bool launcherEnabled: !running ||
28 readonly property bool launcherEnabled: !running ||
29 (!paused && loader.target === leftComponent)
29 (!paused && loader.target === leftComponent)
30− readonly property bool stagesEnabled: !running
30+ readonly property bool spreadEnabled: !running
31 readonly property bool panelEnabled: !running
31 readonly property bool panelEnabled: !running
32 readonly property bool panelContentEnabled: !running
32 readonly property bool panelContentEnabled: !running
33 readonly property bool running: loader.sourceComponent !== null
33 readonly property bool running: loader.sourceComponent !== null
@@ -126,11 +126,24 @@ Item {
126
126
127 onFinished: {
127 onFinished: {
128 root.launcher.hide();
128 root.launcher.hide();
129− loader.load(bottomComponent);
129+ loader.load(rightComponent);
130 }
130 }
131 }
131 }
132 }
132 }
133
133
134+ Component {
135+ id: rightComponent
136+ TutorialRight {
137+ objectName: "tutorialRight"
138+ parent: root.stages
139+ anchors.fill: parent
140+ edgeSize: root.edgeSize
141+ panel: root.panel
142+
143+ onFinished: loader.load(bottomComponent)
144+ }
145+ }
146+
134 Component {
147 Component {
135 id: bottomComponent
148 id: bottomComponent
136 TutorialBottom {
149 TutorialBottom {
qml/Tutorial/TutorialLeftFinish.qml+3−9
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19+import "." as LocalComponents
19
20
20TutorialPage {
21TutorialPage {
21 id: root
22 id: root
@@ -26,21 +27,14 @@ TutorialPage {
26
27
27 foreground {
28 foreground {
28 children: [
29 children: [
29− Image {
30+ LocalComponents.Tick {
30 objectName: "tick"
31 objectName: "tick"
31 anchors {
32 anchors {
32 horizontalCenter: parent.horizontalCenter
33 horizontalCenter: parent.horizontalCenter
33 top: parent.top
34 top: parent.top
34 topMargin: root.textBottom + units.gu(3)
35 topMargin: root.textBottom + units.gu(3)
35 }
36 }
36− source: Qt.resolvedUrl("graphics/tick.png")
37− height: units.gu(6.5)
38− width: units.gu(6.5)
39−
40− MouseArea {
41− anchors.fill: parent
42− onClicked: root.hide()
37+ onClicked: root.hide()
43− }
44 }
38 }
45 ]
39 ]
46 }
40 }
qml/Tutorial/TutorialPage.qml+49−44
@@ -55,8 +55,8 @@ Showable {
55 property real textXOffset: 0
55 property real textXOffset: 0
56 property real textYOffset: 0
56 property real textYOffset: 0
57
57
58− // Foreground opacity
58+ // Foreground text opacity
59− property real foregroundOpacity: 1
59+ property real textOpacity: 1
60
60
61 signal finished()
61 signal finished()
62
62
@@ -125,53 +125,58 @@ Showable {
125 Item {
125 Item {
126 id: foreground
126 id: foreground
127 anchors.fill: parent
127 anchors.fill: parent
128− opacity: root.foregroundOpacity < 1 ? root.foregroundOpacity : root._foregroundHideOpacity
128+ opacity: root._foregroundHideOpacity
129−
129+
130+ Item {
131+ anchors.fill: parent
132+ opacity: root.textOpacity
133+
130− Label {
134+ Label {
131− id: titleLabel
135+ id: titleLabel
132− anchors {
136+ anchors {
133− top: parent.verticalCenter
137+ top: parent.verticalCenter
134− topMargin: d.verticalOffset + root.textYOffset
138+ topMargin: d.verticalOffset + root.textYOffset
135− left: parent.left
139+ left: parent.left
136− leftMargin: d.sideMargin + d.textXOffset
140+ leftMargin: d.sideMargin + d.textXOffset
141+ }
142+ width: parent.width - d.sideMargin * 2
143+ horizontalAlignment: Text.AlignLeft
144+ wrapMode: Text.Wrap
145+ font.weight: Font.Light
146+ font.pixelSize: units.gu(3.5)
137 }
147 }
138− width: parent.width - d.sideMargin * 2
139− horizontalAlignment: Text.AlignLeft
140− wrapMode: Text.Wrap
141− font.weight: Font.Light
142− font.pixelSize: units.gu(3.5)
143− }
144
148
145− Label {
149+ Label {
146− id: textLabel
150+ id: textLabel
147− anchors {
151+ anchors {
148− top: titleLabel.bottom
152+ top: titleLabel.bottom
149− topMargin: units.gu(2)
153+ topMargin: units.gu(2)
150− left: parent.left
154+ left: parent.left
151− leftMargin: d.sideMargin + d.textXOffset
155+ leftMargin: d.sideMargin + d.textXOffset
156+ }
157+ width: (parent.width - d.sideMargin * 2) * (fullTextWidth ? 1 : 0.66)
158+ horizontalAlignment: Text.AlignLeft
159+ wrapMode: Text.Wrap
160+ font.weight: Font.Light
161+ font.pixelSize: units.gu(2.5)
152 }
162 }
153− width: (parent.width - d.sideMargin * 2) * (fullTextWidth ? 1 : 0.66)
154− horizontalAlignment: Text.AlignLeft
155− wrapMode: Text.Wrap
156− font.weight: Font.Light
157− font.pixelSize: units.gu(2.5)
158− }
159
163
160− Label {
164+ Label {
161− id: errorLabel
165+ id: errorLabel
162− objectName: "errorLabel"
166+ objectName: "errorLabel"
163− anchors {
167+ anchors {
164− top: parent.verticalCenter
168+ top: parent.verticalCenter
165− topMargin: d.verticalOffset + root.textYOffset
169+ topMargin: d.verticalOffset + root.textYOffset
166− left: parent.left
170+ left: parent.left
167− leftMargin: d.sideMargin + d.textXOffset
171+ leftMargin: d.sideMargin + d.textXOffset
172+ }
173+ width: parent.width - d.sideMargin * 2
174+ horizontalAlignment: Text.AlignLeft
175+ wrapMode: Text.Wrap
176+ font.weight: Font.Light
177+ font.pixelSize: units.gu(3.5)
178+ opacity: 0
168 }
179 }
169− width: parent.width - d.sideMargin * 2
170− horizontalAlignment: Text.AlignLeft
171− wrapMode: Text.Wrap
172− font.weight: Font.Light
173− font.pixelSize: units.gu(3.5)
174− opacity: 0
175 }
180 }
176
181
177 // A place for subclasses to add extra widgets
182 // A place for subclasses to add extra widgets
qml/Tutorial/TutorialRight.qmlnew file+219
@@ -0,0 +1,219 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.3
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import Unity.Application 0.1
21+import "../Components"
22+import "../Stages"
23+import "." as LocalComponents
24+
25+TutorialPage {
26+ id: root
27+
28+ property var panel
29+ property alias edgeSize: stage.dragAreaWidth
30+
31+ title: i18n.tr("To view open apps")
32+ text: i18n.tr("Long swipe from the right edge.")
33+ errorText: i18n.tr("You almost got it!") + "\n" + i18n.tr("Try again.")
34+
35+ textOpacity: 1 - slider.percent
36+
37+ SequentialAnimation {
38+ paused: root.paused
39+ running: false // !slider.active
40+ loops: Animation.Infinite
41+
42+ UbuntuNumberAnimation {
43+ target: stage
44+ property: "x"
45+ to: -units.gu(2)
46+ duration: UbuntuAnimation.SleepyDuration
47+ }
48+ UbuntuNumberAnimation {
49+ target: stage
50+ property: "x"
51+ to: 0
52+ duration: UbuntuAnimation.SleepyDuration
53+ }
54+ }
55+
56+ foreground {
57+ children: [
58+ LocalComponents.Slider {
59+ id: slider
60+ anchors {
61+ right: parent.right
62+ top: parent.top
63+ topMargin: root.textBottom + units.gu(3)
64+ }
65+ rotation: 180
66+ offset: stage.dragProgress - stage.x
67+ active: stage.dragging
68+ },
69+
70+ // Just assume PhoneStage for now. The tablet version of the right-edge
71+ // tutorial is still being spec'd by the design team.
72+ PhoneStage {
73+ id: stage
74+ anchors.top: parent.top
75+ x: 0
76+ width: parent.width
77+ height: parent.height
78+ applicationManager: fakeAppManager
79+ color: "transparent"
80+ interactive: false
81+ altTabEnabled: false
82+ focusFirstApp: false
83+ startScale: 0.9
84+ endScale: 0.6
85+
86+ onOpened: {
87+ overlay.show();
88+ root.textOpacity = 0;
89+ slider.visible = false;
90+ }
91+
92+ onDraggingChanged: {
93+ if (!dragging && !overlay.shown) {
94+ root.showError();
95+ }
96+ }
97+ },
98+
99+ Showable {
100+ id: overlay
101+ objectName: "overlay"
102+ anchors.fill: parent
103+
104+ opacity: 0
105+ shown: false
106+ showAnimation: UbuntuNumberAnimation { property: "opacity"; to: 1 }
107+
108+ Label {
109+ anchors.top: parent.top
110+ anchors.topMargin: root.panel.panelHeight + units.gu(2)
111+ anchors.left: parent.left
112+ anchors.leftMargin: units.gu(2)
113+ anchors.right: parent.right
114+ anchors.rightMargin: units.gu(2)
115+ wrapMode: Text.Wrap
116+ horizontalAlignment: Text.AlignHCenter
117+ fontSize: "large"
118+ text: i18n.tr("View all your running tasks.")
119+ }
120+
121+ LocalComponents.Tick {
122+ objectName: "tick"
123+ anchors.bottom: bottomOverlayText.top
124+ anchors.bottomMargin: units.gu(1)
125+ anchors.horizontalCenter: bottomOverlayText.horizontalCenter
126+ onClicked: root.hide()
127+ }
128+
129+ Label {
130+ id: bottomOverlayText
131+ anchors.bottom: parent.bottom
132+ anchors.bottomMargin: units.gu(2)
133+ anchors.left: parent.left
134+ anchors.leftMargin: units.gu(2)
135+ anchors.right: parent.right
136+ anchors.rightMargin: units.gu(2)
137+ wrapMode: Text.Wrap
138+ horizontalAlignment: Text.AlignHCenter
139+ fontSize: "small"
140+ text: i18n.tr("Tap here to continue.")
141+ }
142+ }
143+ ]
144+ }
145+
146+ ListModel {
147+ id: fakeAppManager
148+
149+ readonly property string focusedApplicationId: "facebook"
150+
151+ function focusApplication(appId) {}
152+ function requestFocusApplication(appId) {}
153+ function findApplication(appId) {return null;}
154+
155+ signal applicationAdded(string appId)
156+ signal applicationRemoved(string appId)
157+ signal focusRequested(string appId)
158+
159+ ListElement {
160+ appId: "facebook"
161+ fullscreen: false
162+ name: ""
163+ icon: ""
164+ state: ApplicationInfoInterface.Stopped
165+ splashTitle: ""
166+ splashImage: ""
167+ splashShowHeader: false
168+ splashColor: "transparent"
169+ splashColorHeader: "transparent"
170+ splashColorFooter: "transparent"
171+ defaultScreenshot: "../Tutorial/graphics/facebook.png"
172+ }
173+
174+ ListElement {
175+ appId: "camera"
176+ fullscreen: false
177+ name: ""
178+ icon: ""
179+ state: ApplicationInfoInterface.Stopped
180+ splashTitle: ""
181+ splashImage: ""
182+ splashShowHeader: false
183+ splashColor: "transparent"
184+ splashColorHeader: "transparent"
185+ splashColorFooter: "transparent"
186+ defaultScreenshot: "../Tutorial/graphics/camera.png"
187+ }
188+
189+ ListElement {
190+ appId: "gallery"
191+ fullscreen: false
192+ name: ""
193+ icon: ""
194+ state: ApplicationInfoInterface.Stopped
195+ splashTitle: ""
196+ splashImage: ""
197+ splashShowHeader: false
198+ splashColor: "transparent"
199+ splashColorHeader: "transparent"
200+ splashColorFooter: "transparent"
201+ defaultScreenshot: "../Tutorial/graphics/gallery.png"
202+ }
203+
204+ ListElement {
205+ appId: "dialer"
206+ fullscreen: false
207+ name: ""
208+ icon: ""
209+ state: ApplicationInfoInterface.Stopped
210+ splashTitle: ""
211+ splashImage: ""
212+ splashShowHeader: false
213+ splashColor: "transparent"
214+ splashColorHeader: "transparent"
215+ splashColorFooter: "transparent"
216+ defaultScreenshot: "../Tutorial/graphics/dialer.jpg"
217+ }
218+ }
219+}
qml/Tutorial/graphics/camera.pngnew file

Binary file not shown.

qml/Tutorial/graphics/dialer.jpgnew file

Binary file not shown.

qml/Tutorial/graphics/facebook.pngnew file

Binary file not shown.

qml/Tutorial/graphics/gallery.pngnew file

Binary file not shown.

tests/qmltests/Tutorial/tst_Tutorial.qml+27
@@ -211,6 +211,33 @@ Item {
211 var tick = findChild(page, "tick");
211 var tick = findChild(page, "tick");
212 tap(tick);
212 tap(tick);
213
213
214+ page = waitForPage("tutorialRight");
215+ checkTopEdge();
216+ checkLeftEdge();
217+ checkBottomEdge();
218+ if (name === "tutorialRight") return page;
219+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
220+ var overlay = findChild(page, "overlay");
221+ tryCompare(overlay, "shown", true);
222+ var tick = findChild(page, "tick");
223+ tap(tick);
224+
225+ var page = waitForPage("tutorialBottom");
226+ checkTopEdge();
227+ checkLeftEdge();
228+ checkRightEdge();
229+ if (name === "tutorialBottom") return page;
230+ touchFlick(shell, halfWidth, shell.height, halfWidth, halfHeight);
231+
232+ var page = waitForPage("tutorialBottomFinish");
233+ checkTopEdge();
234+ checkLeftEdge();
235+ checkRightEdge();
236+ checkBottomEdge();
237+ if (name === "tutorialBottomFinish") return page;
238+ var tick = findChild(page, "tick");
239+ tap(tick);
240+
214 checkFinished();
241 checkFinished();
215 return null;
242 return null;
216 }
243 }