on xenial help sign in
Redesign tutorial to match latest spec (just removing obsolete pages and redesigning look, no new pages yet)
2422d6dMichael Terry
debian/unity8.install+1
@@ -10,5 +10,6 @@ usr/share/unity8/Launcher
10usr/share/unity8/Panel
10usr/share/unity8/Panel
11usr/share/unity8/Shell.qml
11usr/share/unity8/Shell.qml
12usr/share/unity8/Stages
12usr/share/unity8/Stages
13+usr/share/unity8/Tutorial
13usr/share/unity8/Wizard
14usr/share/unity8/Wizard
14usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
15usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
qml/CMakeLists.txt+3−2
@@ -9,10 +9,11 @@ set(QML_DIRS
9 Dash
9 Dash
10 graphics
10 graphics
11 Greeter
11 Greeter
12− Panel
13 Launcher
12 Launcher
14− Stages
15 Notifications
13 Notifications
14+ Panel
15+ Stages
16+ Tutorial
16 Wizard
17 Wizard
17 )
18 )
18
19
qml/Components/EdgeDemo.qmldeleted−254
@@ -1,254 +0,0 @@
1−/*
2− * Copyright (C) 2013 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 AccountsService 0.1
18−import LightDM 0.1 as LightDM
19−import QtQuick 2.0
20−import Ubuntu.Components 1.1
21−
22−Item {
23− id: demo
24−
25− property Item greeter
26− property Item launcher
27− property Item panel
28− property Item stages
29−
30− property bool launcherEnabled: true
31− property bool stagesEnabled: true
32− property bool panelEnabled: true
33− property bool panelContentEnabled: true
34− property bool running: !launcherEnabled || !stagesEnabled || !panelEnabled || !panelContentEnabled
35−
36− property bool paused: false
37−
38− onPausedChanged: {
39− if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
40− if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
41− if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
42− if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
43− if (d.finalEdgeDemo) d.finalEdgeDemo.paused = paused
44− }
45−
46− function hideEdgeDemoInShell() {
47− AccountsService.demoEdges = false;
48− stopDemo();
49− }
50−
51− function hideEdgeDemoInGreeter() {
52− // TODO: AccountsService.demoEdges = false as lightdm user
53− }
54−
55− function hideEdgeDemos() {
56− hideEdgeDemoInGreeter();
57− hideEdgeDemoInShell();
58− }
59−
60− function stopDemo() {
61− launcherEnabled = true
62− stagesEnabled = true
63− panelEnabled = true
64− panelContentEnabled = true
65−
66− // Use a tiny delay for these destroy() calls because if a lot is
67− // happening at once (like creating and being destroyed in same event
68− // loop, as might happen when answering a call while demo is open),
69− // the destroy() call will be ignored.
70− if (d.rightEdgeDemo) d.rightEdgeDemo.destroy(1);
71− if (d.topEdgeDemo) d.topEdgeDemo.destroy(1);
72− if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy(1);
73− if (d.leftEdgeDemo) d.leftEdgeDemo.destroy(1);
74− if (d.finalEdgeDemo) d.finalEdgeDemo.destroy(1);
75− }
76−
77− function startDemo() {
78− if (!d.overlay) {
79− d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
80− }
81−
82− launcherEnabled = false;
83− stagesEnabled = false;
84− panelEnabled = false;
85− panelContentEnabled = false;
86−
87− // Begin with either greeter or top, depending on which is visible
88− if (greeter && greeter.shown) {
89− startRightEdgeDemo()
90− } else {
91− startTopEdgeDemo()
92− }
93− }
94−
95− QtObject {
96− id: d
97− property Component overlay
98− property QtObject rightEdgeDemo
99− property QtObject topEdgeDemo
100− property QtObject bottomEdgeDemo
101− property QtObject leftEdgeDemo
102− property QtObject finalEdgeDemo
103− property bool showEdgeDemo: AccountsService.demoEdges
104− property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
105−
106− function restartDemo() {
107− stopDemo()
108− if (d.showEdgeDemo) {
109− startDemo()
110− }
111− }
112−
113− onShowEdgeDemoChanged: restartDemo()
114− }
115−
116− Connections {
117− target: i18n
118− onLanguageChanged: d.restartDemo()
119− }
120−
121− function startRightEdgeDemo() {
122− if (demo.greeter) {
123− d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
124− "edge": "right",
125− "title": i18n.tr("Right edge"),
126− "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
127− "anchors.fill": demo.greeter,
128− });
129− }
130− if (d.rightEdgeDemo) {
131− d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
132− } else {
133− stopDemo();
134− }
135− }
136−
137− Connections {
138− target: demo.greeter
139−
140− function hide() {
141− if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
142− d.rightEdgeDemo.hide()
143− hideEdgeDemoInGreeter()
144− startTopEdgeDemo()
145− }
146− }
147−
148− onUnlocked: hide()
149− onShownChanged: if (!greeter.shown) hide()
150− }
151−
152− function startTopEdgeDemo() {
153− demo.panelEnabled = true;
154− if (demo.stages) {
155− d.topEdgeDemo = d.overlay.createObject(demo.panel, {
156− "edge": "top",
157− "title": i18n.tr("Top edge"),
158− "text": i18n.tr("Try swiping from the top edge to access the indicators"),
159− "anchors.fill": demo.panel,
160− });
161− }
162− if (d.topEdgeDemo) {
163− d.topEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
164− } else {
165− stopDemo();
166− }
167− }
168−
169− Connections {
170− target: demo.panel.indicators
171− onFullyOpenedChanged: {
172− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
173− d.topEdgeDemo.hideNow()
174− startBottomEdgeDemo()
175− }
176− }
177− }
178−
179− function startBottomEdgeDemo() {
180− if (demo.panel.indicators) {
181− d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
182− "edge": "bottom",
183− "title": i18n.tr("Close"),
184− "text": i18n.tr("Swipe up again to close the settings screen"),
185− "anchors.fill": demo.panel.indicators,
186− });
187− }
188− if (d.bottomEdgeDemo) {
189− d.bottomEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
190− } else {
191− stopDemo();
192− }
193− }
194−
195− Connections {
196− target: demo.panel.indicators
197− onPartiallyOpenedChanged: {
198− if (d.bottomEdgeDemo &&
199− d.bottomEdgeDemo.available &&
200− !demo.panel.indicators.partiallyOpened &&
201− !demo.panel.indicators.fullyOpened) {
202− d.bottomEdgeDemo.hideNow()
203− startLeftEdgeDemo()
204− }
205− }
206− }
207−
208− function startLeftEdgeDemo() {
209− demo.panelEnabled = false;
210− demo.launcherEnabled = true;
211− if (demo.stages) {
212− d.leftEdgeDemo = d.overlay.createObject(demo.stages, {
213− "edge": "left",
214− "title": i18n.tr("Left edge"),
215− "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
216− "anchors.fill": demo.stages,
217− });
218− }
219− if (d.leftEdgeDemo) {
220− d.leftEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
221− } else {
222− stopDemo();
223− }
224− }
225−
226− Connections {
227− target: demo.launcher
228− onStateChanged: {
229− if (d.leftEdgeDemo && d.leftEdgeDemo.available && launcher.state == "visible") {
230− d.leftEdgeDemo.hide()
231− launcher.hide()
232− startFinalEdgeDemo()
233− }
234− }
235− }
236−
237− function startFinalEdgeDemo() {
238− demo.launcherEnabled = false;
239− if (demo.stages) {
240− d.finalEdgeDemo = d.overlay.createObject(demo.stages, {
241− "edge": "none",
242− "title": i18n.tr("Well done"),
243− "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),
244− "anchors.fill": demo.stages,
245− "showSkip": false,
246− });
247− }
248− if (d.finalEdgeDemo) {
249− d.finalEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
250− } else {
251− stopDemo();
252− }
253− }
254−}
qml/Components/EdgeDemoOverlay.qmldeleted−273
@@ -1,273 +0,0 @@
1−/*
2− * Copyright (C) 2013 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 Powerd 0.1
18−import QtQuick 2.0
19−import QtGraphicalEffects 1.0
20−import Unity.Application 0.1
21−import Ubuntu.Components 0.1
22−
23−Showable {
24− id: overlay
25−
26− /*
27− * Valid values are "left", "right", "top", "bottom", or "none".
28− */
29− property string edge: "top"
30−
31− /*
32− * This is the header displayed, like "Right edge".
33− */
34− property alias title: titleLabel.text
35−
36− /*
37− * This is the block of text displayed below the header.
38− */
39− property alias text: textLabel.text
40−
41− /*
42− * This is the text for the skip button.
43− */
44− property alias skipText: skipLabel.text
45−
46− /*
47− * This is the visible status of the skip button.
48− */
49− property alias showSkip: skipLabel.visible
50−
51− /*
52− * Whether this demo is running currently.
53− */
54− readonly property bool active: available && visible
55−
56− /*
57− * Whether animations are paused.
58− */
59− property alias paused: wholeAnimation.paused
60−
61− /*
62− * Whether animations are running.
63− */
64− readonly property alias running: wholeAnimation.running
65−
66− signal skip()
67−
68− function doSkip() {
69− d.skipOnHide = true;
70− hide();
71− }
72−
73− function hideNow() {
74− overlay.visible = false;
75− overlay.available = false;
76− if (d.skipOnHide) {
77− overlay.skip();
78− }
79− }
80−
81− showAnimation: StandardAnimation {
82− property: "opacity"
83− to: 1
84− onRunningChanged: if (running) overlay.visible = true
85− }
86− hideAnimation: StandardAnimation {
87− property: "opacity"
88− to: 0
89− duration: UbuntuAnimation.BriskDuration
90− onRunningChanged: if (!running) overlay.hideNow()
91− }
92−
93− QtObject {
94− id: d
95− property bool skipOnHide: false
96− property int edgeMargin: units.gu(4)
97− }
98−
99− Rectangle {
100− objectName: "backgroundShade"
101−
102− anchors.fill: parent
103− color: "black"
104− opacity: 0.8
105− visible: overlay.active
106−
107− MouseArea {
108− objectName: "backgroundShadeMouseArea"
109−
110− anchors.fill: parent
111− enabled: overlay.edge == "none" && overlay.opacity == 1.0
112− onClicked: overlay.doSkip()
113− }
114− }
115−
116− Item {
117− id: hintGroup
118− x: 0
119− y: 0
120− width: parent.width
121− height: parent.height
122− visible: overlay.active
123−
124− Column {
125− id: labelGroup
126− spacing: units.gu(3)
127−
128− anchors {
129− margins: d.edgeMargin
130− left: parent.left
131− top: overlay.edge == "bottom" ? undefined : parent.top
132− bottom: overlay.edge == "bottom" ? parent.bottom : undefined
133− }
134−
135− Label {
136− id: titleLabel
137− fontSize: "x-large"
138− width: units.gu(25)
139− wrapMode: Text.WordWrap
140− }
141−
142− Label {
143− id: textLabel
144− width: units.gu(25)
145− wrapMode: Text.WordWrap
146− }
147−
148− Label {
149− id: skipLabel
150− objectName: "skipLabel"
151− text: i18n.tr("Skip intro")
152− color: UbuntuColors.orange
153− fontSize: "small"
154−
155− Icon {
156− anchors.left: parent.right
157− anchors.verticalCenter: parent.verticalCenter
158− height: units.dp(12)
159− width: units.dp(12)
160− name: "chevron"
161− color: UbuntuColors.orange
162− }
163−
164− MouseArea {
165− // Make clickable area bigger than just the link because
166− // otherwise, the edge demo will feel hard to dismiss.
167− anchors.fill: parent
168− anchors.margins: -units.gu(5)
169− onClicked: overlay.doSkip()
170− }
171− }
172− }
173−
174− LinearGradient {
175− id: edgeHint
176− property int size: 1
177− cached: false
178− visible: overlay.edge != "none"
179− gradient: Gradient {
180− GradientStop {
181− position: 0.0
182− color: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4) // UbuntuColors.orange, but transparent
183− }
184− GradientStop {
185− position: 1.0
186− color: "transparent"
187− }
188− }
189− anchors.fill: parent
190− start: {
191− if (overlay.edge == "right") {
192− return Qt.point(width, 0);
193− } else if (overlay.edge == "left") {
194− return Qt.point(0, 0);
195− } else if (overlay.edge == "top") {
196− return Qt.point(0, 0);
197− } else {
198− return Qt.point(0, height);
199− }
200− }
201− end: {
202− if (overlay.edge == "right") {
203− return Qt.point(width - size, 0);
204− } else if (overlay.edge == "left") {
205− return Qt.point(size, 0);
206− } else if (overlay.edge == "top") {
207− return Qt.point(0, size);
208− } else {
209− return Qt.point(0, height - size);
210− }
211− }
212− }
213− }
214−
215− SequentialAnimation {
216− id: wholeAnimation
217− objectName: "wholeAnimation"
218− running: overlay.active
219−
220− ParallelAnimation {
221− id: fadeInAnimation
222−
223− StandardAnimation {
224− target: labelGroup
225− property: {
226− if (overlay.edge == "right" || overlay.edge == "left") {
227− return "anchors.leftMargin";
228− } else if (overlay.edge == "bottom") {
229− return "anchors.bottomMargin";
230− } else {
231− return "anchors.topMargin";
232− }
233− }
234− from: {
235− if (overlay.edge == "right") {
236− return d.edgeMargin + units.gu(3)
237− } else {
238− return d.edgeMargin - units.gu(3)
239− }
240− }
241− to: d.edgeMargin
242− duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
243− }
244− StandardAnimation {
245− target: overlay
246− property: "opacity"
247− from: 0.0
248− to: 1.0
249− duration: UbuntuAnimation.SleepyDuration
250− }
251− }
252−
253− SequentialAnimation {
254− id: hintAnimation
255− loops: Animation.Infinite
256− property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
257− property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
258− property double maxGlow: units.dp(20)
259− property int duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
260−
261− ParallelAnimation {
262− StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
263− StandardAnimation { target: edgeHint; property: "size"; from: 1; to: hintAnimation.maxGlow; duration: hintAnimation.duration }
264− }
265−
266− // Undo the above
267− ParallelAnimation {
268− StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: hintAnimation.endVal; to: 0; duration: hintAnimation.duration }
269− StandardAnimation { target: edgeHint; property: "size"; from: hintAnimation.maxGlow; to: 1; duration: hintAnimation.duration }
270− }
271− }
272− }
273−}
qml/Launcher/Launcher.qml+13−7
@@ -26,6 +26,7 @@ Item {
26 property bool autohideEnabled: false
26 property bool autohideEnabled: false
27 property bool available: true // can be used to disable all interactions
27 property bool available: true // can be used to disable all interactions
28 property alias inverted: panel.inverted
28 property alias inverted: panel.inverted
29+ property bool shadeBackground: true // can be used to disable background shade when launcher is visible
29
30
30 property int panelWidth: units.gu(8)
31 property int panelWidth: units.gu(8)
31 property int dragAreaWidth: units.gu(1)
32 property int dragAreaWidth: units.gu(1)
@@ -33,6 +34,10 @@ Item {
33 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
34 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
34 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
35 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
35
36
37+ readonly property bool dragging: dragArea.dragging
38+ readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
39+ readonly property real visibleWidth: panel.width + panel.x
40+
36 readonly property bool shown: panel.x > -panel.width
41 readonly property bool shown: panel.x > -panel.width
37
42
38 // emitted when an application is selected
43 // emitted when an application is selected
@@ -154,7 +159,7 @@ Item {
154
159
155 MouseArea {
160 MouseArea {
156 id: launcherDragArea
161 id: launcherDragArea
157− enabled: root.state == "visible"
162+ enabled: root.available && root.state == "visible"
158 anchors.fill: panel
163 anchors.fill: panel
159 anchors.rightMargin: -units.gu(2)
164 anchors.rightMargin: -units.gu(2)
160 drag {
165 drag {
@@ -180,7 +185,7 @@ Item {
180 right: parent.right
185 right: parent.right
181 bottom: parent.bottom
186 bottom: parent.bottom
182 }
187 }
183− enabled: root.state == "visible"
188+ enabled: root.shadeBackground && root.state == "visible"
184 onPressed: {
189 onPressed: {
185 root.state = ""
190 root.state = ""
186 }
191 }
@@ -190,7 +195,7 @@ Item {
190 id: backgroundShade
195 id: backgroundShade
191 anchors.fill: parent
196 anchors.fill: parent
192 color: "black"
197 color: "black"
193− opacity: root.state == "visible" ? 0.6 : 0
198+ opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
194
199
195 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
200 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
196 }
201 }
@@ -198,14 +203,14 @@ Item {
198 LauncherPanel {
203 LauncherPanel {
199 id: panel
204 id: panel
200 objectName: "launcherPanel"
205 objectName: "launcherPanel"
201− enabled: root.available
206+ enabled: root.available && root.state == "visible"
202 width: root.panelWidth
207 width: root.panelWidth
203 anchors {
208 anchors {
204 top: parent.top
209 top: parent.top
205 bottom: parent.bottom
210 bottom: parent.bottom
206 }
211 }
207 x: -width
212 x: -width
208− visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
213+ visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
209 model: LauncherModel
214 model: LauncherModel
210
215
211 property bool animate: true
216 property bool animate: true
@@ -247,6 +252,7 @@ Item {
247 direction: Direction.Rightwards
252 direction: Direction.Rightwards
248
253
249 enabled: root.available
254 enabled: root.available
255+ x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
250 width: root.dragAreaWidth
256 width: root.dragAreaWidth
251 height: root.height
257 height: root.height
252
258
@@ -260,7 +266,7 @@ Item {
260 // would appear right next to the user's finger out of nowhere.
266 // would appear right next to the user's finger out of nowhere.
261 // Instead, we make the panel go towards the user's finger in several
267 // Instead, we make the panel go towards the user's finger in several
262 // steps. ie., in an animated way.
268 // steps. ie., in an animated way.
263− var targetPanelX = Math.min(0, touchX - panel.width)
269+ var targetPanelX = Math.min(0, touchX - panel.width) - root.x
264 var delta = targetPanelX - panel.x
270 var delta = targetPanelX - panel.x
265 // the trick is not to go all the way (1.0) as it would cause a sudden jump
271 // the trick is not to go all the way (1.0) as it would cause a sudden jump
266 panel.x += 0.4 * delta
272 panel.x += 0.4 * delta
@@ -292,7 +298,7 @@ Item {
292 name: "visible"
298 name: "visible"
293 PropertyChanges {
299 PropertyChanges {
294 target: panel
300 target: panel
295− x: 0
301+ x: -root.x // so we never go past panelWidth, even when teased by tutorial
296 }
302 }
297 },
303 },
298 State {
304 State {
qml/Shell.qml+33−23
@@ -36,6 +36,7 @@ import "Panel"
36import "Components"
36import "Components"
37import "Notifications"
37import "Notifications"
38import "Stages"
38import "Stages"
39+import "Tutorial"
39import "Wizard"
40import "Wizard"
40import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Notifications 1.0 as NotificationBackend
41import Unity.Session 0.1
42import Unity.Session 0.1
@@ -62,7 +63,7 @@ Item {
62
63
63 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
64 readonly property alias hasLockedApp: greeter.hasLockedApp
65 readonly property alias hasLockedApp: greeter.hasLockedApp
65− readonly property bool forcedUnlock: edgeDemo.running
66+ readonly property bool forcedUnlock: tutorial.running
66 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
67 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
67
68
68 property bool sideStageEnabled: shell.width >= units.gu(100)
69 property bool sideStageEnabled: shell.width >= units.gu(100)
@@ -161,7 +162,7 @@ Item {
161
162
162 ScreenGrabber {
163 ScreenGrabber {
163 id: screenGrabber
164 id: screenGrabber
164− z: edgeDemo.z + 10
165+ z: dialogs.z + 10
165 enabled: Powerd.status === Powerd.On
166 enabled: Powerd.status === Powerd.On
166 }
167 }
167
168
@@ -252,14 +253,16 @@ Item {
252 }
253 }
253
254
254 onApplicationAdded: {
255 onApplicationAdded: {
255− if (greeter.shown && appId != "unity8-dash") {
256+ if (appId != "unity8-dash") {
257+ if (greeter.shown) {
256− greeter.startUnlock()
258+ greeter.startUnlock();
259+ }
257
260
258 // If this happens on first boot, we may be in edge
261 // If this happens on first boot, we may be in edge
259 // tutorial or wizard while receiving a call. But a call
262 // tutorial or wizard while receiving a call. But a call
260 // is more important than wizard so just bail out of those.
263 // is more important than wizard so just bail out of those.
261− if (edgeDemo.running) {
264+ if (tutorial.running) {
262− edgeDemo.hideEdgeDemos();
265+ tutorial.finish();
263 wizard.hide();
266 wizard.hide();
264 }
267 }
265 }
268 }
@@ -287,7 +290,7 @@ Item {
287 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
290 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
288 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
291 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
289
292
290− property bool interactive: edgeDemo.stagesEnabled
293+ property bool interactive: tutorial.stagesEnabled
291 && !greeter.shown
294 && !greeter.shown
292 && !lockscreen.shown
295 && !lockscreen.shown
293 && panel.indicators.fullyClosed
296 && panel.indicators.fullyClosed
@@ -320,7 +323,7 @@ Item {
320 Binding {
323 Binding {
321 target: applicationsDisplayLoader.item
324 target: applicationsDisplayLoader.item
322 property: "spreadEnabled"
325 property: "spreadEnabled"
323− value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
326+ value: tutorial.stagesEnabled && !greeter.hasLockedApp
324 }
327 }
325 Binding {
328 Binding {
326 target: applicationsDisplayLoader.item
329 target: applicationsDisplayLoader.item
@@ -653,9 +656,13 @@ Item {
653 LauncherModel.setUser(user);
656 LauncherModel.setUser(user);
654 }
657 }
655
658
659+ onTapped: {
660+ if (!tutorial.running) {
656− onTapped: launcher.tease()
661+ launcher.tease();
662+ }
663+ }
657 onDraggingChanged: {
664 onDraggingChanged: {
658− if (dragging) {
665+ if (dragging && !tutorial.running) {
659 launcher.tease();
666 launcher.tease();
660 }
667 }
661 }
668 }
@@ -693,7 +700,7 @@ Item {
693
700
694 onStatusChanged: {
701 onStatusChanged: {
695 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
702 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
696− !callManager.hasCalls && !edgeDemo.running) {
703+ !callManager.hasCalls && !tutorial.running) {
697 // We don't want to simply call greeter.showNow() here, because
704 // We don't want to simply call greeter.showNow() here, because
698 // that will take too long. Qt will delay button event
705 // that will take too long. Qt will delay button event
699 // handling until the greeter is done loading and may think the
706 // handling until the greeter is done loading and may think the
@@ -709,7 +716,7 @@ Item {
709 }
716 }
710
717
711 function showHome() {
718 function showHome() {
712− if (edgeDemo.running) {
719+ if (tutorial.running) {
713 return
720 return
714 }
721 }
715
722
@@ -751,8 +758,8 @@ Item {
751 anchors.fill: parent //because this draws indicator menus
758 anchors.fill: parent //because this draws indicator menus
752 indicators {
759 indicators {
753 hides: [launcher]
760 hides: [launcher]
754− available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
761+ available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
755− contentEnabled: edgeDemo.panelContentEnabled
762+ contentEnabled: tutorial.panelContentEnabled
756 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
763 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
757
764
758 minimizedPanelHeight: units.gu(3)
765 minimizedPanelHeight: units.gu(3)
@@ -785,8 +792,9 @@ Item {
785 anchors.bottom: parent.bottom
792 anchors.bottom: parent.bottom
786 width: parent.width
793 width: parent.width
787 dragAreaWidth: shell.edgeSize
794 dragAreaWidth: shell.edgeSize
788− available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
795+ available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
789 inverted: usageModeSettings.usageMode === "Staged"
796 inverted: usageModeSettings.usageMode === "Staged"
797+ shadeBackground: !tutorial.running
790
798
791 onShowDashHome: showHome()
799 onShowDashHome: showHome()
792 onDash: showDash()
800 onDash: showDash()
@@ -799,7 +807,7 @@ Item {
799 if (greeter.hasLockedApp) {
807 if (greeter.hasLockedApp) {
800 greeter.startUnlock()
808 greeter.startUnlock()
801 }
809 }
802− if (!edgeDemo.running)
810+ if (!tutorial.running)
803 shell.activateApplication(appId)
811 shell.activateApplication(appId)
804 }
812 }
805 onShownChanged: {
813 onShownChanged: {
@@ -872,15 +880,17 @@ Item {
872 }
880 }
873 }
881 }
874
882
875− EdgeDemo {
883+ Tutorial {
876− id: edgeDemo
884+ id: tutorial
877− objectName: "edgeDemo"
885+ objectName: "tutorial"
878− z: dialogs.z + 10
886+ active: AccountsService.demoEdges
879− paused: Powerd.status === Powerd.Off || wizard.active // Saves power
887+ paused: LightDM.Greeter.active
880− greeter: greeter
881 launcher: launcher
888 launcher: launcher
882 panel: panel
889 panel: panel
883 stages: stages
890 stages: stages
891+ overlay: overlay
892+
893+ onFinished: AccountsService.demoEdges = false
884 }
894 }
885
895
886 Connections {
896 Connections {
@@ -890,7 +900,7 @@ Item {
890
900
891 Rectangle {
901 Rectangle {
892 id: shutdownFadeOutRectangle
902 id: shutdownFadeOutRectangle
893− z: edgeDemo.z + 10
903+ z: screenGrabber.z + 10
894 enabled: false
904 enabled: false
895 visible: false
905 visible: false
896 color: "black"
906 color: "black"
qml/Tutorial/Arrow.qmlnew file+56
@@ -0,0 +1,56 @@
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+
20+Item {
21+ id: root
22+
23+ property alias color: circle.color
24+
25+ // Will make whole arrow darker
26+ property real darkenBy: 0
27+
28+ property alias chevronOpacity: chevron.opacity
29+
30+ ////
31+
32+ Rectangle {
33+ id: circle
34+ anchors.fill: parent
35+ radius: width / 2
36+ }
37+
38+ Image {
39+ id: chevron
40+ anchors.centerIn: parent
41+ source: Qt.resolvedUrl("graphics/chevron.png")
42+ fillMode: Image.PreserveAspectFit
43+ sourceSize.width: 152
44+ sourceSize.height: 152
45+ width: parent.width / 2
46+ height: parent.height / 2
47+ }
48+
49+ Rectangle {
50+ id: darkCircle
51+ anchors.fill: parent
52+ radius: width / 2
53+ color: "black"
54+ opacity: root.darkenBy
55+ }
56+}
qml/Tutorial/Slider.qmlnew file+117
@@ -0,0 +1,117 @@
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+
20+Item {
21+ id: root
22+
23+ // Whether this slider is short or long
24+ property bool shortSwipe
25+
26+ // How far the user has slid
27+ property real offset
28+
29+ // Set to true when slider is being used
30+ property bool active
31+
32+ // How far in percentage terms
33+ readonly property real percent: d.slideOffset / target.x
34+
35+ QtObject {
36+ id: d
37+ readonly property color trayColor: "#424141"
38+ readonly property real margin: units.gu(0.5)
39+ readonly property real arrowSize: root.height - margin * 2
40+ readonly property real dotSize: units.dp(1)
41+ readonly property real slideOffset: Math.min(root.offset - offscreenOffset, target.x)
42+ readonly property real offscreenOffset: units.gu(2)
43+ }
44+
45+ implicitWidth: shortSwipe ? units.gu(15) : units.gu(27.5)
46+ implicitHeight: units.gu(6.5)
47+
48+ Rectangle {
49+ color: d.trayColor
50+ anchors.fill: parent
51+ anchors.rightMargin: clipBox.width - 1
52+ }
53+
54+ // We want to have a circular border around the target. But we can't just
55+ // do a radius on two of a rectangle's corners. So we clip a full circle.
56+ Item {
57+ id: clipBox
58+
59+ clip: true
60+ anchors.top: parent.top
61+ anchors.bottom: parent.bottom
62+ anchors.right: parent.right
63+ width: parent.height / 2
64+
65+ Rectangle {
66+ color: d.trayColor
67+ anchors.top: parent.top
68+ anchors.bottom: parent.bottom
69+ anchors.right: parent.right
70+ width: parent.width * 2
71+ radius: parent.width
72+ }
73+ }
74+
75+ Arrow {
76+ id: target
77+ width: d.arrowSize
78+ height: d.arrowSize
79+ color: "#73000000"
80+ chevronOpacity: 0.52
81+ anchors.right: parent.right
82+ anchors.rightMargin: d.margin
83+ anchors.verticalCenter: parent.verticalCenter
84+ }
85+
86+ Row {
87+ anchors.left: handle.horizontalCenter
88+ anchors.right: target.horizontalCenter
89+ anchors.verticalCenter: parent.verticalCenter
90+
91+ layoutDirection: Qt.RightToLeft
92+ spacing: d.dotSize * 2
93+
94+ Repeater {
95+ model: parent.width / (parent.spacing + d.dotSize)
96+ Rectangle {
97+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
98+ height: d.dotSize
99+ width: height
100+ radius: width
101+ color: "white"
102+ opacity: 0.2
103+ }
104+ }
105+ }
106+
107+ Arrow {
108+ id: handle
109+ width: d.arrowSize
110+ height: d.arrowSize
111+ color: UbuntuColors.orange
112+ darkenBy: root.active ? 0.5 : 0
113+ anchors.left: parent.left
114+ anchors.leftMargin: d.slideOffset
115+ anchors.verticalCenter: parent.verticalCenter
116+ }
117+}
qml/Tutorial/Tutorial.qmlnew file+85
@@ -0,0 +1,85 @@
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+
20+Item {
21+ id: root
22+
23+ property alias active: loader.active
24+ property bool paused
25+
26+ property Item launcher
27+ property Item panel
28+ property Item stages
29+ property Item overlay
30+
31+ readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
32+ readonly property bool stagesEnabled: loader.item ? loader.item.stagesEnabled : true
33+ readonly property bool panelEnabled: loader.item ? loader.item.panelEnabled : true
34+ readonly property bool panelContentEnabled: loader.item ? loader.item.panelContentEnabled : true
35+ readonly property bool running: loader.item ? loader.item.running : false
36+
37+ function finish() {
38+ if (loader.item) {
39+ loader.item.finish();
40+ }
41+ }
42+
43+ signal finished()
44+
45+ Loader {
46+ id: loader
47+ anchors.fill: parent
48+ source: "TutorialContent.qml"
49+
50+ Binding {
51+ target: loader.item
52+ property: "paused"
53+ value: root.paused
54+ }
55+
56+ Binding {
57+ target: loader.item
58+ property: "launcher"
59+ value: root.launcher
60+ }
61+
62+ Binding {
63+ target: loader.item
64+ property: "panel"
65+ value: root.panel
66+ }
67+
68+ Binding {
69+ target: loader.item
70+ property: "stages"
71+ value: root.stages
72+ }
73+
74+ Binding {
75+ target: loader.item
76+ property: "overlay"
77+ value: root.overlay
78+ }
79+
80+ Connections {
81+ target: loader.item
82+ onFinished: root.finished()
83+ }
84+ }
85+}
qml/Tutorial/TutorialContent.qmlnew file+130
@@ -0,0 +1,130 @@
1+/*
2+ * Copyright (C) 2013,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+
20+Item {
21+ id: root
22+
23+ property Item launcher
24+ property Item panel
25+ property Item stages
26+ property Item overlay
27+
28+ readonly property bool launcherEnabled: !running ||
29+ (!paused && loader.target === leftComponent)
30+ readonly property bool stagesEnabled: !running
31+ readonly property bool panelEnabled: !running
32+ readonly property bool panelContentEnabled: !running
33+ readonly property bool running: loader.sourceComponent !== null
34+
35+ property bool paused: false
36+
37+ signal finished()
38+
39+ function finish() {
40+ d.stop();
41+ finished();
42+ }
43+
44+ ////
45+
46+ Component.onCompleted: {
47+ d.start();
48+ }
49+
50+ QtObject {
51+ id: d
52+
53+ function stop() {
54+ loader.sourceComponent = null;
55+ }
56+
57+ function start() {
58+ loader.load(leftComponent);
59+ }
60+ }
61+
62+ Loader {
63+ id: loader
64+ objectName: "tutorialLoader"
65+
66+ property Component target: {
67+ if (next) {
68+ return next;
69+ } else if (loader.item && loader.item.shown) {
70+ return sourceComponent;
71+ } else {
72+ return null;
73+ }
74+ }
75+
76+ property Component next: null
77+
78+ function load(comp) {
79+ if (loader.item) {
80+ next = comp;
81+ loader.item.hide();
82+ } else {
83+ loader.sourceComponent = comp;
84+ }
85+ }
86+
87+ Connections {
88+ target: loader.item
89+ onFinished: {
90+ loader.sourceComponent = loader.next;
91+ if (loader.next != null) {
92+ loader.next = null;
93+ } else {
94+ root.finished();
95+ }
96+ }
97+ }
98+
99+ Binding {
100+ target: loader.item
101+ property: "paused"
102+ value: root.paused
103+ }
104+ }
105+
106+ Component {
107+ id: leftComponent
108+ TutorialLeft {
109+ objectName: "tutorialLeft"
110+ parent: root.stages
111+ anchors.fill: parent
112+ launcher: root.launcher
113+
114+ onFinished: loader.load(leftFinishComponent)
115+ }
116+ }
117+
118+ Component {
119+ id: leftFinishComponent
120+ TutorialLeftFinish {
121+ objectName: "tutorialLeftFinish"
122+ parent: root.stages
123+ anchors.fill: parent
124+ textXOffset: root.launcher.panelWidth
125+ backgroundFadesOut: true
126+
127+ onFinished: root.launcher.hide()
128+ }
129+ }
130+}
qml/Tutorial/TutorialLeft.qmlnew file+91
@@ -0,0 +1,91 @@
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 "." as LocalComponents
20+
21+TutorialPage {
22+ id: root
23+
24+ property var launcher
25+
26+ title: i18n.tr("Open the launcher")
27+ text: i18n.tr("Short swipe from the left edge.")
28+
29+ textXOffset: root.launcher.x + root.launcher.visibleWidth
30+
31+ Connections {
32+ target: root.launcher
33+
34+ onStateChanged: {
35+ if (root.launcher.state === "visible") {
36+ finishTimer.start();
37+ }
38+ }
39+
40+ onDash: {
41+ finishTimer.stop();
42+ root.showError();
43+ root.launcher.hide();
44+ }
45+ }
46+
47+ SequentialAnimation {
48+ id: teaseAnimation
49+ paused: running && root.paused
50+ running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
51+ loops: Animation.Infinite
52+
53+ UbuntuNumberAnimation {
54+ target: root.launcher
55+ property: "x"
56+ to: units.gu(2)
57+ duration: UbuntuAnimation.SleepyDuration
58+ }
59+ UbuntuNumberAnimation {
60+ target: root.launcher
61+ property: "x"
62+ to: 0
63+ duration: UbuntuAnimation.SleepyDuration
64+ }
65+ }
66+
67+ Timer {
68+ id: finishTimer
69+ interval: 1
70+ onTriggered: {
71+ root.hide();
72+ root.launcher.x = 0; // make sure to reset launcher before we go
73+ }
74+ }
75+
76+ foreground {
77+ children: [
78+ LocalComponents.Slider {
79+ id: slider
80+ anchors {
81+ left: parent.left
82+ top: parent.top
83+ topMargin: root.textBottom + units.gu(3)
84+ }
85+ offset: root.launcher.x + root.launcher.visibleWidth + root.launcher.progress
86+ active: root.launcher.dragging
87+ shortSwipe: true
88+ }
89+ ]
90+ }
91+}
qml/Tutorial/TutorialLeftFinish.qmlnew file+47
@@ -0,0 +1,47 @@
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+
20+TutorialPage {
21+ id: root
22+
23+ title: i18n.tr("These are the shortcuts to favorite apps")
24+ text: i18n.tr("Tap here to finish.")
25+ fullTextWidth: true
26+
27+ foreground {
28+ children: [
29+ Image {
30+ objectName: "tick"
31+ anchors {
32+ horizontalCenter: parent.horizontalCenter
33+ top: parent.top
34+ topMargin: root.textBottom + units.gu(3)
35+ }
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()
43+ }
44+ }
45+ ]
46+ }
47+}
qml/Tutorial/TutorialPage.qmlnew file+240
@@ -0,0 +1,240 @@
1+/*
2+ * Copyright (C) 2013,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 "../Components"
20+
21+Showable {
22+ id: root
23+
24+ // This is the header displayed, like "Right edge"
25+ property alias title: titleLabel.text
26+
27+ // This is the block of text displayed below the header
28+ property alias text: textLabel.text
29+
30+ // Whether animations are paused
31+ property bool paused
32+
33+ // Whether to give the text the full width that the title has
34+ property bool fullTextWidth
35+
36+ // Whether whole page (background + foreground) or just the foreground fades in
37+ property bool backgroundFadesIn: false
38+
39+ // Whether whole page (background + foreground) or just the foreground fades out
40+ property bool backgroundFadesOut: false
41+
42+ // The foreground Item, add children to it that you want to fade in
43+ property alias foreground: foregroundExtra
44+
45+ // The text label bottom, so you can position elements relative to it
46+ readonly property real textBottom: Math.max(textLabel.y + textLabel.height, errorTextLabel.y + errorTextLabel.height)
47+
48+ // The MouseArea that eats events (so you can adjust size as you will)
49+ property alias mouseArea: mouseArea
50+
51+ // X/Y offsets for text
52+ property real textXOffset: 0
53+ property real textYOffset: 0
54+
55+ // Foreground opacity
56+ property real foregroundOpacity: 1
57+
58+ signal finished()
59+
60+ function showError() {
61+ errorTimer.start();
62+ }
63+
64+ ////
65+
66+ shown: false
67+ Component.onCompleted: show()
68+
69+ property real _foregroundHideOpacity
70+
71+ showAnimation: StandardAnimation {
72+ property: root.backgroundFadesIn ? "opacity" : "_foregroundHideOpacity"
73+ from: 0
74+ to: 1
75+ duration: root.backgroundFadesIn ? UbuntuAnimation.SleepyDuration : UbuntuAnimation.BriskDuration
76+ }
77+
78+ hideAnimation: StandardAnimation {
79+ property: root.backgroundFadesOut ? "opacity" : "_foregroundHideOpacity"
80+ to: 0
81+ duration: UbuntuAnimation.BriskDuration
82+ onRunningChanged: {
83+ if (!running) {
84+ root.finished();
85+ }
86+ }
87+ }
88+
89+ QtObject {
90+ id: d
91+
92+ readonly property real sideMargin: units.gu(5.5)
93+ readonly property real verticalOffset: -units.gu(9)
94+ readonly property real textXOffset: Math.max(0, root.textXOffset - sideMargin + units.gu(2))
95+
96+ property real fadeInOffset: {
97+ if (showAnimation.running) {
98+ var opacity = root[root.showAnimation.property]
99+ return (1 - opacity) * units.gu(3);
100+ } else {
101+ return 0;
102+ }
103+ }
104+ }
105+
106+ Timer {
107+ id: errorTimer
108+ interval: 3500
109+ }
110+
111+ MouseArea { // eat any errant presses
112+ id: mouseArea
113+ anchors.fill: parent
114+ }
115+
116+ Rectangle {
117+ anchors.fill: parent
118+ color: "black"
119+ opacity: 0.82
120+ }
121+
122+ Item {
123+ id: foreground
124+ anchors.fill: parent
125+ opacity: root.foregroundOpacity < 1 ? root.foregroundOpacity : root._foregroundHideOpacity
126+
127+ Label {
128+ id: titleLabel
129+ anchors {
130+ top: parent.verticalCenter
131+ topMargin: d.verticalOffset + root.textYOffset
132+ left: parent.left
133+ leftMargin: d.sideMargin + d.textXOffset
134+ }
135+ width: parent.width - d.sideMargin * 2
136+ horizontalAlignment: Text.AlignLeft
137+ wrapMode: Text.Wrap
138+ font.weight: Font.Light
139+ font.pixelSize: units.gu(3.5)
140+ }
141+
142+ Label {
143+ id: textLabel
144+ anchors {
145+ top: titleLabel.bottom
146+ topMargin: units.gu(2)
147+ left: parent.left
148+ leftMargin: d.sideMargin + d.textXOffset
149+ }
150+ width: (parent.width - d.sideMargin * 2) * (fullTextWidth ? 1 : 0.66)
151+ horizontalAlignment: Text.AlignLeft
152+ wrapMode: Text.Wrap
153+ font.weight: Font.Light
154+ font.pixelSize: units.gu(2.5)
155+ }
156+
157+ // We use two separate labels like this rather than just changing
158+ // the text of the above labels because we want to know where to place
159+ // sliders (via root.textBottom) without having that place change
160+ // as the text changes length.
161+ Label {
162+ id: errorTitleLabel
163+ objectName: "errorTitleLabel"
164+ anchors {
165+ top: titleLabel.top
166+ left: titleLabel.left
167+ }
168+ width: titleLabel.width
169+ horizontalAlignment: titleLabel.horizontalAlignment
170+ wrapMode: titleLabel.wrapMode
171+ font.weight: titleLabel.font.weight
172+ font.pixelSize: titleLabel.font.pixelSize
173+ opacity: 0
174+ text: i18n.tr("You almost got it!")
175+ }
176+
177+ Label {
178+ id: errorTextLabel
179+ objectName: "errorTextLabel"
180+ anchors {
181+ top: errorTitleLabel.bottom
182+ topMargin: textLabel.anchors.topMargin
183+ left: textLabel.left
184+ }
185+ width: textLabel.width
186+ horizontalAlignment: textLabel.horizontalAlignment
187+ wrapMode: textLabel.wrapMode
188+ font.weight: textLabel.font.weight
189+ font.pixelSize: textLabel.font.pixelSize
190+ opacity: 0
191+ text: i18n.tr("Try again.")
192+ }
193+
194+ // A place for subclasses to add extra widgets
195+ Item {
196+ id: foregroundExtra
197+ anchors.fill: parent
198+ }
199+ }
200+
201+ states: State {
202+ name: "errorState"
203+ when: errorTimer.running
204+ PropertyChanges { target: titleLabel; opacity: 0 }
205+ PropertyChanges { target: textLabel; opacity: 0 }
206+ PropertyChanges { target: errorTitleLabel; opacity: 1 }
207+ PropertyChanges { target: errorTextLabel; opacity: 1 }
208+ }
209+
210+ transitions: Transition {
211+ to: "errorState"
212+ reversible: true
213+ SequentialAnimation {
214+ ParallelAnimation {
215+ StandardAnimation {
216+ target: titleLabel
217+ property: "opacity"
218+ duration: UbuntuAnimation.BriskDuration
219+ }
220+ StandardAnimation {
221+ target: textLabel
222+ property: "opacity"
223+ duration: UbuntuAnimation.BriskDuration
224+ }
225+ }
226+ ParallelAnimation {
227+ StandardAnimation {
228+ target: errorTitleLabel
229+ property: "opacity"
230+ duration: UbuntuAnimation.BriskDuration
231+ }
232+ StandardAnimation {
233+ target: errorTextLabel
234+ property: "opacity"
235+ duration: UbuntuAnimation.BriskDuration
236+ }
237+ }
238+ }
239+ }
240+}
qml/Tutorial/graphics/chevron.pngnew file

Binary file not shown.

qml/Tutorial/graphics/tick.pngnew file

Binary file not shown.

tests/autopilot/unity8/shell/emulators/edges_demo.pydeleted−140
@@ -1,140 +0,0 @@
1−# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2−#
3−# Unity Autopilot Test Suite
4−# Copyright (C) 2014 Canonical
5−#
6−# This program is free software: you can redistribute it and/or modify
7−# it under the terms of the GNU General Public License as published by
8−# the Free Software Foundation, either version 3 of the License, or
9−# (at your option) any later version.
10−#
11−# This program is distributed in the hope that it will be useful,
12−# but WITHOUT ANY WARRANTY; without even the implied warranty of
13−# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14−# GNU General Public License for more details.
15−#
16−# You should have received a copy of the GNU General Public License
17−# along with this program. If not, see <http://www.gnu.org/licenses/>.
18−#
19−
20−import logging
21−import time
22−
23−import ubuntuuitoolkit
24−
25−import autopilot
26−from autopilot import introspection
27−
28−
29−logger = logging.getLogger(__name__)
30−
31−
32−class RightEdgeDemoOverlay(
33− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
34−
35− @classmethod
36− def validate_dbus_object(cls, path, state):
37− name = introspection.get_classname_from_path(path)
38− if name == b'EdgeDemoOverlay':
39− if state['edge'][1] == 'right':
40− return True
41− return False
42−
43− @autopilot.logging.log_action(logger.info)
44− def swipe(self):
45− """Swipe to the left to complete this demo step."""
46− x, y, width, height = self.globalRect
47− start_x = x + width
48− stop_x = x
49− start_y = stop_y = y + height // 2
50− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
51− return self.get_root_instance().wait_select_single(
52− edge='top', active=True)
53−
54−
55−class TopEdgeDemoOverlay(
56− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
57−
58− @classmethod
59− def validate_dbus_object(cls, path, state):
60− name = introspection.get_classname_from_path(path)
61− if name == b'EdgeDemoOverlay':
62− if state['edge'][1] == 'top':
63− return True
64− return False
65−
66− @autopilot.logging.log_action(logger.info)
67− def swipe(self):
68− """Swipe to the bottom to complete this demo step."""
69− x, y, width, height = self.globalRect
70− start_x = stop_x = x + width // 2
71− start_y = y
72− stop_y = y + height
73− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
74− return self.get_root_instance().wait_select_single(
75− edge='bottom', active=True)
76−
77−
78−class BottomEdgeDemoOverlay(
79− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
80−
81− @classmethod
82− def validate_dbus_object(cls, path, state):
83− name = introspection.get_classname_from_path(path)
84− if name == b'EdgeDemoOverlay':
85− if state['edge'][1] == 'bottom':
86− return True
87− return False
88−
89− @autopilot.logging.log_action(logger.info)
90− def swipe(self):
91− """Swipe to the top to complete this demo step."""
92− x, y, width, height = self.globalRect
93− start_x = stop_x = x + width // 2
94− start_y = y + height
95− stop_y = y
96− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
97− return self.get_root_instance().wait_select_single(
98− edge='left', active=True)
99−
100−
101−class LeftEdgeDemoOverlay(
102− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
103−
104− @classmethod
105− def validate_dbus_object(cls, path, state):
106− name = introspection.get_classname_from_path(path)
107− if name == b'EdgeDemoOverlay':
108− if state['edge'][1] == 'left':
109− return True
110− return False
111−
112− @autopilot.logging.log_action(logger.info)
113− def swipe(self):
114− """Swipe to the right to complete this demo step."""
115− x, y, width, height = self.globalRect
116− start_x = x
117− stop_x = x + width
118− start_y = stop_y = y + height // 2
119− self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
120− return self.get_root_instance().wait_select_single(
121− edge='none', active=True)
122−
123−
124−class FinalEdgeDemoOverlay(
125− ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
126−
127− @classmethod
128− def validate_dbus_object(cls, path, state):
129− name = introspection.get_classname_from_path(path)
130− if name == b'EdgeDemoOverlay':
131− if state['edge'][1] == 'none':
132− return True
133− return False
134−
135− @autopilot.logging.log_action(logger.info)
136− def tap_to_start(self):
137− """Tap to finish the demo and start using the Ubuntu Touch."""
138− time.sleep(1)
139− self.pointing_device.click_object(self)
140− self.shown.wait_for(False)
tests/autopilot/unity8/shell/emulators/tutorial.pynew file+54
@@ -0,0 +1,54 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2014 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import logging
21+import time
22+
23+import ubuntuuitoolkit
24+
25+import autopilot
26+from autopilot import introspection
27+
28+
29+logger = logging.getLogger(__name__)
30+
31+
32+class TutorialPage(
33+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
34+
35+ @classmethod
36+ def validate_dbus_object(cls, path, state):
37+ name = introspection.get_classname_from_path(path)
38+ return name == b'TutorialPage' or name == b'TutorialLeft'
39+
40+ @autopilot.logging.log_action(logger.info)
41+ def short_swipe_right(self):
42+ x, y, width, height = self.globalRect
43+ start_x = x
44+ stop_x = x + width // 3
45+ start_y = stop_y = y + height // 2
46+ self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
47+ self.shown.wait_for(False)
48+
49+ @autopilot.logging.log_action(logger.info)
50+ def tap(self):
51+ """Tap the tick button to complete this step."""
52+ button = self.select_single(objectName="tick")
53+ self.pointing_device.click_object(button)
54+ self.shown.wait_for(False)
tests/autopilot/unity8/shell/fixture_setup.py+6−6
@@ -54,7 +54,7 @@ class FakeScopes(fixtures.Fixture):
54 return ld_library_path
54 return ld_library_path
55
55
56
56
57−class EdgesDemo(fixtures.Fixture):
57+class Tutorial(fixtures.Fixture):
58
58
59 def __init__(self, enable):
59 def __init__(self, enable):
60 super().__init__()
60 super().__init__()
@@ -62,12 +62,12 @@ class EdgesDemo(fixtures.Fixture):
62
62
63 def setUp(self):
63 def setUp(self):
64 super().setUp()
64 super().setUp()
65− original_state = self._is_edges_demo_enabled()
65+ original_state = self._is_tutorial_enabled()
66 if self.enable != original_state:
66 if self.enable != original_state:
67− self.addCleanup(self._set_edges_demo, original_state)
67+ self.addCleanup(self._set_tutorial, original_state)
68− self._set_edges_demo(self.enable)
68+ self._set_tutorial(self.enable)
69
69
70− def _is_edges_demo_enabled(self):
70+ def _is_tutorial_enabled(self):
71 command = [
71 command = [
72 'dbus-send', '--system', '--print-reply',
72 'dbus-send', '--system', '--print-reply',
73 '--dest=org.freedesktop.Accounts',
73 '--dest=org.freedesktop.Accounts',
@@ -79,7 +79,7 @@ class EdgesDemo(fixtures.Fixture):
79 output = subprocess.check_output(command, universal_newlines=True)
79 output = subprocess.check_output(command, universal_newlines=True)
80 return True if output.count('true') else False
80 return True if output.count('true') else False
81
81
82− def _set_edges_demo(self, value):
82+ def _set_tutorial(self, value):
83 value_string = 'true' if value else 'false'
83 value_string = 'true' if value else 'false'
84 command = [
84 command = [
85 'dbus-send', '--system', '--print-reply',
85 'dbus-send', '--system', '--print-reply',
tests/autopilot/unity8/shell/tests/test_edges_demo.py → tests/autopilot/unity8/shell/tests/test_tutorial.pyrenamed+15−16
@@ -24,28 +24,27 @@ from unity8.shell import (
24 fixture_setup,
24 fixture_setup,
25 tests
25 tests
26)
26)
27−# unused import to load the edge emulators custom proxy objects.
27+# unused import to load the tutorial emulators custom proxy objects.
28−from unity8.shell.emulators import edges_demo # NOQA
28+from unity8.shell.emulators import tutorial # NOQA
29
29
30
30
31−class EdgesDemoTestCase(tests.UnityTestCase):
31+class TutorialTestCase(tests.UnityTestCase):
32
32
33 def setUp(self):
33 def setUp(self):
34− super(EdgesDemoTestCase, self).setUp()
34+ super(TutorialTestCase, self).setUp()
35 self._qml_mock_enabled = False
35 self._qml_mock_enabled = False
36 self._data_dirs_mock_enabled = False
36 self._data_dirs_mock_enabled = False
37
37
38− self.useFixture(fixture_setup.EdgesDemo(True))
38+ self.useFixture(fixture_setup.Tutorial(True))
39 self.unity = self.launch_unity()
39 self.unity = self.launch_unity()
40
40
41− def test_complete_edge_demo(self):
41+ def test_complete_tutorial(self):
42+ greeter = self.main_window.get_greeter()
42− edge_demo = self.unity.select_single('EdgeDemo')
43+ tutorial = self.unity.select_single('Tutorial')
43− self.assertThat(edge_demo.running, Eventually(Equals(True)))
44+ self.assertThat(tutorial.running, Eventually(Equals(True)))
45+ greeter.swipe()
44− right_edge_overlay = self.unity.wait_select_single(
46+ page = self.unity.wait_select_single(objectName='tutorialLeft')
45− edge='right', active=True)
47+ page.short_swipe_right()
46− top_edge_overlay = right_edge_overlay.swipe()
48+ page = self.unity.wait_select_single(objectName='tutorialLeftFinish')
47− bottom_edge_overlay = top_edge_overlay.swipe()
49+ page.tap()
48− left_edge_overlay = bottom_edge_overlay.swipe()
49− final_overlay = left_edge_overlay.swipe()
50− final_overlay.tap_to_start()
51− self.assertThat(edge_demo.running, Eventually(Equals(False)))
50+ self.assertThat(tutorial.running, Eventually(Equals(False)))
tests/qmltests/CMakeLists.txt+1−1
@@ -25,7 +25,6 @@ add_qml_test(Components Background)
25add_qml_test(Components Carousel)
25add_qml_test(Components Carousel)
26add_qml_test(Components Dialogs)
26add_qml_test(Components Dialogs)
27add_qml_test(Components DraggingArea)
27add_qml_test(Components DraggingArea)
28−add_qml_test(Components EdgeDemoOverlay)
29add_qml_test(Components LazyImage)
28add_qml_test(Components LazyImage)
30add_qml_test(Components Lockscreen)
29add_qml_test(Components Lockscreen)
31add_qml_test(Components Rating)
30add_qml_test(Components Rating)
@@ -91,4 +90,5 @@ add_qml_test(Stages SessionContainer ENVIRONMENT)
91add_qml_test(Stages TabletStage)
90add_qml_test(Stages TabletStage)
92add_qml_test(Stages WindowMoveResizeArea)
91add_qml_test(Stages WindowMoveResizeArea)
93add_qml_test(Stages Splash)
92add_qml_test(Stages Splash)
93+add_qml_test(Tutorial Tutorial)
94add_qml_test(Wizard Wizard)
94add_qml_test(Wizard Wizard)
tests/qmltests/Components/tst_EdgeDemoOverlay.qmldeleted−123
@@ -1,123 +0,0 @@
1−/*
2− * Copyright 2013 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.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import "../../../qml/Components"
21−
22−Item {
23− id: root
24− width: boxWidth * 3
25− height: boxHeight * 2
26−
27− property int boxWidth: 250
28− property int boxHeight: 250
29−
30− EdgeDemoOverlay {
31− id: top
32− edge: "top"
33− title: "Top"
34− text: "Displayed on top left"
35− anchors.left: parent.left
36− anchors.top: parent.top
37− width: boxWidth
38− height: boxHeight
39− }
40−
41− EdgeDemoOverlay {
42− id: right
43− edge: "right"
44− title: "Right"
45− text: "Displayed on top right"
46− anchors.right: parent.right
47− anchors.top: parent.top
48− width: boxWidth
49− height: boxHeight
50− }
51−
52− EdgeDemoOverlay {
53− id: left
54− edge: "left"
55− title: "Left"
56− text: "Displayed on bottom right"
57− anchors.right: parent.right
58− anchors.bottom: parent.bottom
59− width: boxWidth
60− height: boxHeight
61− available: false
62− }
63−
64− EdgeDemoOverlay {
65− id: bottom
66− edge: "bottom"
67− title: "Bottom"
68− text: "Displayed on bottom left"
69− anchors.left: parent.left
70− anchors.bottom: parent.bottom
71− width: boxWidth
72− height: boxHeight
73− }
74−
75− EdgeDemoOverlay {
76− id: none
77− edge: "none"
78− title: "None"
79− text: "Displayed on top middle"
80− anchors.horizontalCenter: parent.horizontalCenter
81− anchors.top: parent.top
82− width: boxWidth
83− height: boxHeight
84− }
85−
86− SignalSpy {
87− id: signalSpy
88− }
89−
90− UT.UnityTestCase {
91− name: "EdgeDemoOverlay"
92− when: windowShown
93−
94− function test_animations() {
95− compare(right.running, true)
96−
97− compare(left.running, false)
98− left.available = true
99− compare(left.running, true)
100− }
101−
102− function test_skip() {
103− signalSpy.target = bottom
104− signalSpy.signalName = "skip"
105− signalSpy.clear()
106− var bottomSkip = findChild(bottom, "skipLabel")
107− mousePress(bottomSkip, 1, 1)
108− mouseRelease(bottomSkip, 1, 1)
109− signalSpy.wait()
110− compare(bottom.available, false)
111−
112− // Test that the 'none' edge skips anywhere
113− signalSpy.target = none
114− signalSpy.clear()
115− var backgroundShade = findChild(none, "backgroundShadeMouseArea")
116− tryCompare(backgroundShade, "enabled", true)
117− mousePress(backgroundShade, 1, 1)
118− mouseRelease(backgroundShade, 1, 1)
119− signalSpy.wait()
120− compare(none.available, false)
121− }
122− }
123−}
tests/qmltests/Tutorial/tst_Tutorial.qmlnew file+286
@@ -0,0 +1,286 @@
1+/*
2+ * Copyright (C) 2013,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.0
18+import QtTest 1.0
19+import AccountsService 0.1
20+import LightDM 0.1 as LightDM
21+import Ubuntu.Components 1.1
22+import Unity.Application 0.1
23+import Unity.Test 0.1 as UT
24+
25+import "../../../qml"
26+
27+Item {
28+ id: root
29+ width: shellLoader.width + buttons.width
30+ height: shellLoader.height
31+
32+ QtObject {
33+ id: applicationArguments
34+
35+ function hasGeometry() {
36+ return false;
37+ }
38+
39+ function width() {
40+ return 0;
41+ }
42+
43+ function height() {
44+ return 0;
45+ }
46+ }
47+
48+ Component.onCompleted: {
49+ // must set the mock mode before loading the Shell
50+ LightDM.Greeter.mockMode = "single-pin";
51+ LightDM.Users.mockMode = "single-pin";
52+ shellLoader.active = true;
53+ }
54+
55+ Row {
56+ spacing: 0
57+ anchors.fill: parent
58+
59+ Loader {
60+ id: shellLoader
61+
62+ active: false
63+ width: units.gu(40)
64+ height: units.gu(71)
65+
66+ property bool itemDestroyed: false
67+ sourceComponent: Component {
68+ Shell {
69+ property string indicatorProfile: "phone"
70+
71+ Component.onDestruction: {
72+ shellLoader.itemDestroyed = true;
73+ }
74+ }
75+ }
76+ }
77+
78+ Rectangle {
79+ id: buttons
80+ color: "white"
81+ width: units.gu(30)
82+ height: shellLoader.height
83+
84+ Column {
85+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
86+ spacing: units.gu(1)
87+ Row {
88+ anchors { left: parent.left; right: parent.right }
89+ Button {
90+ text: "Restart Tutorial"
91+ onClicked: {
92+ if (shellLoader.status !== Loader.Ready)
93+ return;
94+
95+ AccountsService.demoEdges = false;
96+ AccountsService.demoEdges = true;
97+ }
98+ }
99+ }
100+ }
101+ }
102+ }
103+
104+ UT.UnityTestCase {
105+ id: testCase
106+ name: "Tutorial"
107+ when: windowShown
108+
109+ property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
110+ property real halfWidth: shell ? shell.width / 2 : 0
111+ property real halfHeight: shell ? shell.height / 2 : 0
112+
113+ function init() {
114+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
115+ swipeAwayGreeter();
116+ AccountsService.demoEdges = false;
117+ AccountsService.demoEdges = true;
118+ }
119+
120+ function cleanup() {
121+ shellLoader.itemDestroyed = false;
122+
123+ shellLoader.active = false;
124+
125+ tryCompare(shellLoader, "status", Loader.Null);
126+ tryCompare(shellLoader, "item", null);
127+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
128+ // item might still be alive. So if we set Loader.active back to true
129+ // again right now we will get the very same Shell instance back. So no reload
130+ // actually took place. Likely because Loader waits until the next event loop
131+ // iteration to do its work. So to ensure the reload, we will wait until the
132+ // Shell instance gets destroyed.
133+ tryCompare(shellLoader, "itemDestroyed", true);
134+
135+ // kill all (fake) running apps
136+ killApps();
137+
138+ // reload our test subject to get it in a fresh state once again
139+ shellLoader.active = true;
140+
141+ tryCompare(shellLoader, "status", Loader.Ready);
142+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
143+ }
144+
145+ function killApps() {
146+ while (ApplicationManager.count > 1) {
147+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
148+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
149+ }
150+ compare(ApplicationManager.count, 1)
151+ }
152+
153+ function swipeAwayGreeter() {
154+ var greeter = findChild(shell, "greeter");
155+ tryCompare(greeter, "showProgress", 1);
156+
157+ touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
158+
159+ // wait until the animation has finished
160+ tryCompare(greeter, "showProgress", 0);
161+ waitForRendering(greeter);
162+ }
163+
164+ function waitForPage(name) {
165+ tryCompareFunction(function() { return findChild(shell, name) !== null; }, true);
166+ waitForRendering(findChild(shell, name));
167+ var page = findChild(shell, name);
168+ tryCompare(page, "shown", true);
169+ tryCompare(page.showAnimation, "running", false);
170+ return page;
171+ }
172+
173+ function checkTopEdge() {
174+ touchFlick(shell, halfWidth, 0, halfWidth, halfHeight);
175+
176+ var panel = findChild(shell, "panel");
177+ tryCompare(panel.indicators, "fullyClosed", true);
178+ }
179+
180+ function checkLeftEdge() {
181+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
182+
183+ var launcher = findChild(shell, "launcher");
184+ tryCompare(launcher, "state", "");
185+ }
186+
187+ function checkRightEdge() {
188+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
189+
190+ var stage = findChild(shell, "stage");
191+ var spreadView = findChild(stage, "spreadView");
192+ tryCompare(spreadView, "phase", 0);
193+ }
194+
195+ function checkBottomEdge() {
196+ // Can't actually check effect of swipe, since dash isn't really loaded
197+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
198+ tryCompare(applicationsDisplayLoader.item, "interactive", false);
199+ }
200+
201+ function checkFinished() {
202+ tryCompare(AccountsService, "demoEdges", false);
203+
204+ var tutorial = findChild(shell, "tutorial");
205+ tryCompare(tutorial, "running", false);
206+
207+ var launcher = findChild(shell, "launcher");
208+ tryCompare(launcher, "shown", false);
209+ }
210+
211+ function goToPage(name) {
212+ var page = waitForPage("tutorialLeft");
213+ checkTopEdge();
214+ checkRightEdge();
215+ checkBottomEdge();
216+ if (name === "tutorialLeft") return page;
217+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
218+
219+ page = waitForPage("tutorialLeftFinish");
220+ if (name === "tutorialLeftFinish") return page;
221+ var tick = findChild(page, "tick");
222+ tap(tick);
223+
224+ checkFinished();
225+ return null;
226+ }
227+
228+ function test_walkthrough() {
229+ goToPage(null);
230+ }
231+
232+ function test_launcherShortDrag() {
233+ // goToPage does a normal launcher pull. But here we want to test
234+ // just barely pulling the launcher out and letting go (i.e. not
235+ // triggering the "progress" property of Launcher).
236+
237+ var left = goToPage("tutorialLeft");
238+
239+ // Make sure we don't do anything if we don't pull the launcher
240+ // out much.
241+ var launcher = findChild(shell, "launcher");
242+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
243+ tryCompare(launcher, "state", ""); // should remain hidden
244+ tryCompare(left, "shown", true); // and we should still be on left
245+
246+ // Now drag out but not past launcher itself
247+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
248+
249+ waitForPage("tutorialLeftFinish");
250+ }
251+
252+ function test_launcherLongDrag() {
253+ // goToPage does a normal launcher pull. But here we want to test
254+ // a full pull across the page.
255+
256+ var left = goToPage("tutorialLeft");
257+
258+ var launcher = findChild(shell, "launcher");
259+ touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
260+
261+ var errorTextLabel = findChild(left, "errorTextLabel");
262+ var errorTitleLabel = findChild(left, "errorTitleLabel");
263+ tryCompare(launcher, "state", ""); // launcher goes away
264+ tryCompare(left, "shown", true); // still on left page
265+ tryCompare(errorTextLabel, "opacity", 1); // show error
266+ tryCompare(errorTitleLabel, "opacity", 1); // show error
267+ }
268+
269+ function test_launcherDragBack() {
270+ // goToPage does a full launcher pull. But here we test pulling
271+ // all the way out, then dragging back into place.
272+
273+ var left = goToPage("tutorialLeft");
274+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight, true, false);
275+ touchFlick(shell, halfWidth, halfHeight, 0, halfHeight, false, true);
276+
277+ tryCompare(left, "shown", true); // and we should still be on left
278+ }
279+
280+ function test_interrupted() {
281+ goToPage("tutorialLeft");
282+ ApplicationManager.startApplication("dialer-app");
283+ checkFinished();
284+ }
285+ }
286+}
tests/qmltests/tst_TabletShell.qml+2−2
@@ -303,8 +303,8 @@ Row {
303 selectUser(data.user)
303 selectUser(data.user)
304
304
305 AccountsService.demoEdges = data.demo
305 AccountsService.demoEdges = data.demo
306− var edgeDemo = findChild(shell, "edgeDemo")
306+ var tutorial = findChild(shell, "tutorial");
307− tryCompare(edgeDemo, "running", data.demo)
307+ tryCompare(tutorial, "running", data.demo);
308
308
309 swipeFromLeftEdge(shell.width * 0.75)
309 swipeFromLeftEdge(shell.width * 0.75)
310 wait(500) // to give time to handle dash() signal from Launcher
310 wait(500) // to give time to handle dash() signal from Launcher