on 75b8c19 help sign in
unity8/qml/Tutorial/TutorialContent.qml
243 lines 9.2 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 || tutorialLeftLoader.shown
34 readonly property bool spreadEnabled: !running || tutorialRightLoader.shown
35 readonly property bool panelEnabled: !running || tutorialTopLoader.shown
36 readonly property bool running: tutorialLeftLoader.shown
37 || tutorialTopLoader.shown
38 || tutorialRightLoader.shown
39 || tutorialBottomLoader.shown
41 signal finished()
43 function finish() {
44 finished();
45 }
47 ////
49 QtObject {
50 id: d
52 // We allow "" because it is briefly empty on startup, and we don't
53 // want to improperly skip any mobile tutorials.
54 property bool mobileScenario: root.usageScenario === "" ||
55 root.usageScenario === "phone" ||
56 root.usageScenario === "tablet"
58 property var focusedApp: ApplicationManager.focusedApplicationId
59 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
60 : null
62 function haveShown(tutorialId) {
63 return AccountsService.demoEdgesCompleted.indexOf(tutorialId) != -1;
64 }
66 property bool endPointsFinished: tutorialRightLoader.skipped &&
67 tutorialBottomLoader.skipped
68 onEndPointsFinishedChanged: if (endPointsFinished) root.finish()
69 }
71 Loader {
72 id: tutorialLeftLoader
73 objectName: "tutorialLeftLoader"
74 anchors.fill: parent
76 readonly property bool skipped: !d.mobileScenario || d.haveShown("left")
77 readonly property bool shown: item && item.shown
78 active: !skipped || (item && item.visible)
79 onSkippedChanged: if (skipped && shown) item.hide()
81 sourceComponent: TutorialLeft {
82 id: tutorialLeft
83 objectName: "tutorialLeft"
84 anchors.fill: parent
85 launcher: root.launcher
86 hides: [launcher, panel.indicators]
87 paused: root.paused
89 isReady: !tutorialLeftLoader.skipped && !paused && !keyboardVisible
91 // Use an idle timer here, because when constructed, all our isReady variables will be false.
92 // Qml needs a moment to copy their values from Tutorial.qml. So we idle it and recheck.
93 Timer {
94 id: tutorialLeftTimer
95 interval: 0
96 onTriggered: if (tutorialLeft.isReady && !tutorialLeft.shown) tutorialLeft.show()
97 }
99 onIsReadyChanged: if (isReady && !shown) tutorialLeftTimer.start()
100 onFinished: AccountsService.markDemoEdgeCompleted("left")
101 }
102 }
104 Loader {
105 id: tutorialTopLoader
106 objectName: "tutorialTopLoader"
107 anchors.fill: parent
109 readonly property bool skipped: !d.mobileScenario || d.haveShown("top")
110 readonly property bool shown: item && item.shown
111 active: !skipped || (item && item.visible)
112 onSkippedChanged: if (skipped && shown) item.hide()
114 sourceComponent: TutorialTop {
115 id: tutorialTop
116 objectName: "tutorialTop"
117 anchors.fill: parent
118 panel: root.panel
119 hides: [launcher, panel.indicators]
120 paused: root.paused
122 isReady: tutorialLeftLoader.skipped && !tutorialTopLoader.skipped &&
123 !paused && !keyboardVisible &&
124 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow
126 // We fire 30s after left edge tutorial, with at least 3s of inactivity
128 InactivityTimer {
129 id: tutorialTopInactivityTimer
130 lastInputTimestamp: root.lastInputTimestamp
131 page: parent
132 }
134 Timer {
135 id: tutorialTopTimer
136 objectName: "tutorialTopTimer"
137 interval: 27000
138 onTriggered: tutorialTopInactivityTimer.start()
139 }
141 onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
142 onFinished: AccountsService.markDemoEdgeCompleted("top")
143 }
144 }
146 Loader {
147 id: tutorialRightLoader
148 objectName: "tutorialRightLoader"
149 anchors.fill: parent
151 readonly property bool skipped: d.haveShown("right")
152 readonly property bool shown: item && item.shown
153 active: !skipped || (item && item.visible)
154 onSkippedChanged: if (skipped && shown) item.hide()
156 sourceComponent: TutorialRight {
157 id: tutorialRight
158 objectName: "tutorialRight"
159 anchors.fill: parent
160 stage: root.stage
161 usageScenario: root.usageScenario
162 hides: [launcher, panel.indicators]
163 paused: root.paused
165 isReady: tutorialTopLoader.skipped && !tutorialRightLoader.skipped &&
166 !paused && !keyboardVisible &&
167 !tutorialBottomLoader.shown && !tutorialBottomLoader.mightShow &&
168 ApplicationManager.count >= 3
170 InactivityTimer {
171 id: tutorialRightInactivityTimer
172 objectName: "tutorialRightInactivityTimer"
173 lastInputTimestamp: root.lastInputTimestamp
174 page: parent
175 }
177 Connections {
178 target: d
179 onFocusedAppChanged: {
180 if (tutorialRight.isReady && !tutorialRight.shown && d.focusedApp
181 && d.focusedApp.state === ApplicationInfoInterface.Starting) {
182 tutorialRight.show();
183 }
184 }
185 }
187 onIsReadyChanged: if (isReady && !shown) tutorialRightInactivityTimer.start()
188 onFinished: AccountsService.markDemoEdgeCompleted("right")
189 }
190 }
192 Loader {
193 id: tutorialBottomLoader
194 objectName: "tutorialBottomLoader"
195 anchors.fill: parent
197 // See TutorialBottom.qml for an explanation of why we only support
198 // certain apps.
199 readonly property var supportedApps: ["address-book-app",
200 "com.ubuntu.calculator_calculator",
201 "dialer-app",
202 "messaging-app"]
203 readonly property bool skipped: {
204 if (!d.mobileScenario) {
205 return true;
206 }
207 for (var i = 0; i < supportedApps.length; i++) {
208 if (!d.haveShown("bottom-" + supportedApps[i])) {
209 return false;
210 }
211 }
212 return true;
213 }
214 readonly property bool shown: item && item.shown
215 readonly property bool haveShownFocusedApp: d.focusedApp &&
216 d.haveShown("bottom-" + d.focusedApp.appId)
217 readonly property bool mightShow: !skipped && d.focusedApp &&
218 supportedApps.indexOf(d.focusedApp.appId) !== -1 &&
219 !haveShownFocusedApp
220 active: !skipped || (item && item.visible)
221 onHaveShownFocusedAppChanged: if (haveShownFocusedApp && shown) hide()
222 onSkippedChanged: if (skipped && shown) item.hide()
224 sourceComponent: TutorialBottom {
225 id: tutorialBottom
226 objectName: "tutorialBottom"
227 anchors.fill: parent
228 hides: [launcher, panel.indicators]
229 paused: root.paused
230 usageScenario: root.usageScenario
231 stage: root.stage
232 application: d.focusedApp
234 isReady: !tutorialBottomLoader.skipped && !paused && !keyboardVisible &&
235 !tutorialTopLoader.shown && !tutorialRightLoader.shown &&
236 tutorialBottomLoader.mightShow &&
237 d.focusedApp.state === ApplicationInfoInterface.Running
239 onIsReadyChanged: if (isReady && !shown) show()
240 onFinished: AccountsService.markDemoEdgeCompleted("bottom-" + d.focusedApp.appId)
241 }
242 }