on 2c34dfd help sign in
unity8/qml/Tutorial/TutorialContent.qml
290 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 &&
92 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
94 // Use an idle timer here, because when constructed, all our isReady variables will be false.
95 // Qml needs a moment to copy their values from Tutorial.qml. So we idle it and recheck.
96 Timer {
97 id: tutorialLeftTimer
98 interval: 0
99 onTriggered: if (tutorialLeft.isReady && !tutorialLeft.shown) tutorialLeft.show()
100 }
102 onIsReadyChanged: if (isReady && !shown) tutorialLeftTimer.start()
103 onFinished: AccountsService.markDemoEdgeCompleted("left")
104 }
105 }
107 Loader {
108 id: tutorialLeftLongLoader
109 objectName: "tutorialLeftLongLoader"
110 anchors.fill: parent
112 readonly property bool skipped: !d.mobileScenario || d.haveShown("left-long")
113 readonly property bool shown: item && item.shown
114 active: !skipped || (item && item.visible)
115 onSkippedChanged: if (skipped && shown) item.hide()
117 sourceComponent: TutorialLeftLong {
118 id: tutorialLeftLong
119 objectName: "tutorialLeftLong"
120 anchors.fill: parent
121 launcher: root.launcher
122 hides: [launcher, panel.indicators]
123 paused: root.paused
125 skipped: tutorialLeftLongLoader.skipped
126 isReady: tutorialLeftLoader.skipped && !skipped && !paused && !keyboardVisible &&
127 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
129 Timer {
130 id: tutorialLeftLongTimer
131 objectName: "tutorialLeftLongTimer"
132 interval: 5000
133 onTriggered: {
134 if (parent.isReady) {
135 if (!parent.shown) {
136 parent.show();
137 }
138 } else if (!parent.skipped) {
139 restart();
140 }
141 }
142 }
144 onIsReadyChanged: if (isReady && !shown) tutorialLeftLongTimer.start()
145 onFinished: AccountsService.markDemoEdgeCompleted("left-long")
146 }
147 }
149 Loader {
150 id: tutorialTopLoader
151 objectName: "tutorialTopLoader"
152 anchors.fill: parent
154 readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
155 readonly property bool shown: item && item.shown
156 active: !skipped || (item && item.visible)
157 onSkippedChanged: if (skipped && shown) item.hide()
159 sourceComponent: TutorialTop {
160 id: tutorialTop
161 objectName: "tutorialTop"
162 anchors.fill: parent
163 panel: root.panel
164 hides: [launcher, panel.indicators]
165 paused: root.paused
167 skipped: tutorialTopLoader.skipped
168 isReady: tutorialLeftLongLoader.skipped && !skipped && !paused && !keyboardVisible &&
169 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
171 // We fire 30s after left edge tutorial, with at least 3s of inactivity
173 InactivityTimer {
174 id: tutorialTopInactivityTimer
175 lastInputTimestamp: root.lastInputTimestamp
176 page: parent
177 }
179 Timer {
180 id: tutorialTopTimer
181 objectName: "tutorialTopTimer"
182 interval: 27000
183 onTriggered: tutorialTopInactivityTimer.start()
184 }
186 onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
187 onFinished: AccountsService.markDemoEdgeCompleted("top")
188 }
189 }
191 Loader {
192 id: tutorialRightLoader
193 objectName: "tutorialRightLoader"
194 anchors.fill: parent
196 readonly property bool skipped: d.haveShown("right")
197 readonly property bool shown: item && item.shown
198 active: !skipped || (item && item.visible)
199 onSkippedChanged: if (skipped && shown) item.hide()
201 sourceComponent: TutorialRight {
202 id: tutorialRight
203 objectName: "tutorialRight"
204 anchors.fill: parent
205 stage: root.stage
206 usageScenario: root.usageScenario
207 hides: [launcher, panel.indicators]
208 paused: root.paused
210 skipped: tutorialRightLoader.skipped
211 isReady: tutorialTopLoader.skipped && !skipped && !paused && !keyboardVisible &&
212 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow &&
213 ApplicationManager.count >= 3
215 InactivityTimer {
216 id: tutorialRightInactivityTimer
217 objectName: "tutorialRightInactivityTimer"
218 lastInputTimestamp: root.lastInputTimestamp
219 page: parent
220 }
222 Connections {
223 target: d
224 onFocusedAppChanged: {
225 if (tutorialRight.isReady && !tutorialRight.shown && d.focusedApp
226 && d.focusedApp.state === ApplicationInfoInterface.Starting) {
227 tutorialRight.show();
228 }
229 }
230 }
232 onIsReadyChanged: if (isReady && !shown) tutorialRightInactivityTimer.start()
233 onFinished: AccountsService.markDemoEdgeCompleted("right")
234 }
235 }
237 Loader {
238 id: tutorialBottomLoader
239 objectName: "tutorialBottomLoader"
240 anchors.fill: parent
242 // See TutorialBottom.qml for an explanation of why we only support
243 // certain apps.
244 readonly property var supportedApps: ["address-book-app",
245 "com.ubuntu.calculator_calculator",
246 "dialer-app",
247 "messaging-app"]
248 readonly property bool skipped: {
249 if (!d.mobileScenario) {
250 return true;
251 }
252 for (var i = 0; i < supportedApps.length; i++) {
253 if (!d.haveShown("bottom-" + supportedApps[i])) {
254 return false;
255 }
256 }
257 return true;
258 }
259 readonly property bool shown: item && item.shown
260 readonly property bool haveShownFocusedApp: d.focusedApp &&
261 d.haveShown("bottom-" + d.focusedApp.appId)
262 readonly property bool mightShow: !skipped && d.focusedApp &&
263 supportedApps.indexOf(d.focusedApp.appId) !== -1 &&
264 !haveShownFocusedApp
265 active: !skipped || (item && item.visible)
266 onHaveShownFocusedAppChanged: if (haveShownFocusedApp && shown) hide()
267 onSkippedChanged: if (skipped && shown) item.hide()
269 sourceComponent: TutorialBottom {
270 id: tutorialBottom
271 objectName: "tutorialBottom"
272 anchors.fill: parent
273 hides: [launcher, panel.indicators]
274 paused: root.paused
275 usageScenario: root.usageScenario
276 stage: root.stage
277 application: d.focusedApp
279 skipped: tutorialBottomLoader.skipped
280 isReady: !tutorialBottomLoader.skipped && !paused && !keyboardVisible &&
281 !tutorialLeftLoader.shown && !tutorialLeftLongLoader.shown &&
282 !tutorialTopLoader.shown && !tutorialRightLoader.shown &&
283 tutorialBottomLoader.mightShow &&
284 d.focusedApp.state === ApplicationInfoInterface.Running
286 onIsReadyChanged: if (isReady && !shown) show()
287 onFinished: AccountsService.markDemoEdgeCompleted("bottom-" + d.focusedApp.appId)
288 }
289 }