on 3df724b help sign in
unity8/qml/Tutorial/TutorialContent.qml
288 lines 11 kB history raw
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 */
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import AccountsService 0.1
20import Unity.Application 0.1
22Item {
23 id: root
25 property Item launcher
26 property Item panel
27 property Item stage
28 property string usageScenario
29 property bool paused
30 property bool keyboardVisible
31 property var lastInputTimestamp
33 readonly property bool launcherEnabled: !running
34 || tutorialLeftLoader.shown
35 || tutorialLeftLongLoader.shown
36 readonly property bool spreadEnabled: !running || tutorialRightLoader.shown
37 readonly property bool panelEnabled: !running || tutorialTopLoader.shown
38 readonly property bool running: tutorialLeftLoader.shown
39 || tutorialTopLoader.shown
40 || tutorialRightLoader.shown
41 || tutorialBottomLoader.shown
43 signal finished()
45 function finish() {
46 finished();
47 }
49 ////
51 QtObject {
52 id: d
54 // We allow "" because it is briefly empty on startup, and we don't
55 // want to improperly skip any mobile tutorials.
56 property bool mobileScenario: root.usageScenario === "" ||
57 root.usageScenario === "phone" ||
58 root.usageScenario === "tablet"
60 property var focusedApp: ApplicationManager.focusedApplicationId
61 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
62 : null
64 function haveShown(tutorialId) {
65 return AccountsService.demoEdgesCompleted.indexOf(tutorialId) != -1;
66 }
68 property bool endPointsFinished: tutorialRightLoader.skipped &&
69 tutorialBottomLoader.skipped
70 onEndPointsFinishedChanged: if (endPointsFinished) root.finish()
71 }
73 Loader {
74 id: tutorialLeftLoader
75 objectName: "tutorialLeftLoader"
76 anchors.fill: parent
78 readonly property bool skipped: !d.mobileScenario || d.haveShown("left")
79 readonly property bool shown: item && item.shown
80 active: !skipped || (item && item.visible)
81 onSkippedChanged: if (skipped && shown) item.hide()
83 sourceComponent: TutorialLeft {
84 id: tutorialLeft
85 objectName: "tutorialLeft"
86 anchors.fill: parent
87 launcher: root.launcher
88 hides: [launcher, panel.indicators]
89 paused: root.paused
91 isReady: !tutorialLeftLoader.skipped && !paused && !keyboardVisible
93 // Use an idle timer here, because when constructed, all our isReady variables will be false.
94 // Qml needs a moment to copy their values from Tutorial.qml. So we idle it and recheck.
95 Timer {
96 id: tutorialLeftTimer
97 interval: 0
98 onTriggered: if (tutorialLeft.isReady && !tutorialLeft.shown) tutorialLeft.show()
99 }
101 onIsReadyChanged: if (isReady && !shown) tutorialLeftTimer.start()
102 onFinished: AccountsService.markDemoEdgeCompleted("left")
103 }
104 }
106 Loader {
107 id: tutorialLeftLongLoader
108 objectName: "tutorialLeftLongLoader"
109 anchors.fill: parent
111 readonly property bool skipped: !d.mobileScenario || d.haveShown("left-long")
112 readonly property bool shown: item && item.shown
113 active: !skipped || (item && item.visible)
114 onSkippedChanged: if (skipped && shown) item.hide()
116 sourceComponent: TutorialLeftLong {
117 id: tutorialLeftLong
118 objectName: "tutorialLeftLong"
119 anchors.fill: parent
120 launcher: root.launcher
121 hides: [launcher, panel.indicators]
122 paused: root.paused
124 skipped: tutorialLeftLongLoader.skipped
125 isReady: tutorialLeftLoader.skipped && !skipped && !paused && !keyboardVisible &&
126 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
128 Timer {
129 id: tutorialLeftLongTimer
130 objectName: "tutorialLeftLongTimer"
131 interval: 5000
132 onTriggered: {
133 if (parent.isReady) {
134 if (!parent.shown) {
135 parent.show();
136 }
137 } else if (!parent.skipped) {
138 restart();
139 }
140 }
141 }
143 onIsReadyChanged: if (isReady && !shown) tutorialLeftLongTimer.start()
144 onFinished: AccountsService.markDemoEdgeCompleted("left-long")
145 }
146 }
148 Loader {
149 id: tutorialTopLoader
150 objectName: "tutorialTopLoader"
151 anchors.fill: parent
153 readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
154 readonly property bool shown: item && item.shown
155 active: !skipped || (item && item.visible)
156 onSkippedChanged: if (skipped && shown) item.hide()
158 sourceComponent: TutorialTop {
159 id: tutorialTop
160 objectName: "tutorialTop"
161 anchors.fill: parent
162 panel: root.panel
163 hides: [launcher, panel.indicators]
164 paused: root.paused
166 skipped: tutorialTopLoader.skipped
167 isReady: tutorialLeftLongLoader.skipped && !skipped && !paused && !keyboardVisible &&
168 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
170 // We fire 30s after left edge tutorial, with at least 3s of inactivity
172 InactivityTimer {
173 id: tutorialTopInactivityTimer
174 lastInputTimestamp: root.lastInputTimestamp
175 page: parent
176 }
178 Timer {
179 id: tutorialTopTimer
180 objectName: "tutorialTopTimer"
181 interval: 27000
182 onTriggered: tutorialTopInactivityTimer.start()
183 }
185 onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
186 onFinished: AccountsService.markDemoEdgeCompleted("top")
187 }
188 }
190 Loader {
191 id: tutorialRightLoader
192 objectName: "tutorialRightLoader"
193 anchors.fill: parent
195 readonly property bool skipped: d.haveShown("right")
196 readonly property bool shown: item && item.shown
197 active: !skipped || (item && item.visible)
198 onSkippedChanged: if (skipped && shown) item.hide()
200 sourceComponent: TutorialRight {
201 id: tutorialRight
202 objectName: "tutorialRight"
203 anchors.fill: parent
204 stage: root.stage
205 usageScenario: root.usageScenario
206 hides: [launcher, panel.indicators]
207 paused: root.paused
209 skipped: tutorialRightLoader.skipped
210 isReady: tutorialTopLoader.skipped && !skipped && !paused && !keyboardVisible &&
211 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow &&
212 ApplicationManager.count >= 3
214 InactivityTimer {
215 id: tutorialRightInactivityTimer
216 objectName: "tutorialRightInactivityTimer"
217 lastInputTimestamp: root.lastInputTimestamp
218 page: parent
219 }
221 Connections {
222 target: d
223 onFocusedAppChanged: {
224 if (tutorialRight.isReady && !tutorialRight.shown && d.focusedApp
225 && d.focusedApp.state === ApplicationInfoInterface.Starting) {
226 tutorialRight.show();
227 }
228 }
229 }
231 onIsReadyChanged: if (isReady && !shown) tutorialRightInactivityTimer.start()
232 onFinished: AccountsService.markDemoEdgeCompleted("right")
233 }
234 }
236 Loader {
237 id: tutorialBottomLoader
238 objectName: "tutorialBottomLoader"
239 anchors.fill: parent
241 // See TutorialBottom.qml for an explanation of why we only support
242 // certain apps.
243 readonly property var supportedApps: ["address-book-app",
244 "com.ubuntu.calculator_calculator",
245 "dialer-app",
246 "messaging-app"]
247 readonly property bool skipped: {
248 if (!d.mobileScenario) {
249 return true;
250 }
251 for (var i = 0; i < supportedApps.length; i++) {
252 if (!d.haveShown("bottom-" + supportedApps[i])) {
253 return false;
254 }
255 }
256 return true;
257 }
258 readonly property bool shown: item && item.shown
259 readonly property bool haveShownFocusedApp: d.focusedApp &&
260 d.haveShown("bottom-" + d.focusedApp.appId)
261 readonly property bool mightShow: !skipped && d.focusedApp &&
262 supportedApps.indexOf(d.focusedApp.appId) !== -1 &&
263 !haveShownFocusedApp
264 active: !skipped || (item && item.visible)
265 onHaveShownFocusedAppChanged: if (haveShownFocusedApp && shown) hide()
266 onSkippedChanged: if (skipped && shown) item.hide()
268 sourceComponent: TutorialBottom {
269 id: tutorialBottom
270 objectName: "tutorialBottom"
271 anchors.fill: parent
272 hides: [launcher, panel.indicators]
273 paused: root.paused
274 usageScenario: root.usageScenario
275 stage: root.stage
276 application: d.focusedApp
278 skipped: tutorialBottomLoader.skipped
279 isReady: !tutorialBottomLoader.skipped && !paused && !keyboardVisible &&
280 !tutorialTopLoader.shown && !tutorialRightLoader.shown &&
281 tutorialBottomLoader.mightShow &&
282 d.focusedApp.state === ApplicationInfoInterface.Running
284 onIsReadyChanged: if (isReady && !shown) show()
285 onFinished: AccountsService.markDemoEdgeCompleted("bottom-" + d.focusedApp.appId)
286 }
287 }