Merge lp:~mterry/unity8/tutorial-refactor into lp:unity8

Proposed by Michael Terry on 2014-10-28
Status: Superseded
Proposed branch: lp:~mterry/unity8/tutorial-refactor
Merge into: lp:unity8
Diff against target: 3227 lines (+1582/-862)
41 files modified
debian/unity8.install (+1/-0)
plugins/Ubuntu/Gestures/TouchGate.cpp (+2/-0)
plugins/Ubuntu/Gestures/TouchGate.h (+2/-0)
qml/CMakeLists.txt (+3/-2)
qml/Components/EdgeDemo.qml (+0/-254)
qml/Components/EdgeDemoOverlay.qml (+0/-273)
qml/Launcher/Launcher.qml (+13/-7)
qml/Shell.qml (+42/-23)
qml/Stages/ApplicationWindow.qml (+3/-1)
qml/Stages/PhoneStage.qml (+2/-0)
qml/Stages/SessionContainer.qml (+24/-9)
qml/Stages/SpreadDelegate.qml (+2/-1)
qml/Stages/SurfaceContainer.qml (+11/-25)
qml/Stages/TabletStage.qml (+14/-0)
qml/Stages/TransformedTabletSpreadDelegate.qml (+1/-1)
qml/Tutorial/Arrow.qml (+56/-0)
qml/Tutorial/Slider.qml (+117/-0)
qml/Tutorial/Tutorial.qml (+85/-0)
qml/Tutorial/TutorialContent.qml (+130/-0)
qml/Tutorial/TutorialLeft.qml (+91/-0)
qml/Tutorial/TutorialLeftFinish.qml (+47/-0)
qml/Tutorial/TutorialPage.qml (+240/-0)
tests/autopilot/unity8/shell/emulators/tutorial.py (+16/-102)
tests/autopilot/unity8/shell/fixture_setup.py (+6/-6)
tests/autopilot/unity8/shell/tests/test_tutorial.py (+17/-18)
tests/mocks/Unity/Application/MirSurfaceItem.qml (+16/-1)
tests/qmltests/CMakeLists.txt (+2/-1)
tests/qmltests/Components/tst_EdgeDemoOverlay.qml (+0/-123)
tests/qmltests/Stages/ApplicationCheckBox.qml (+45/-0)
tests/qmltests/Stages/RecursingChildSessionControl.qml (+3/-0)
tests/qmltests/Stages/tst_ApplicationWindow.qml (+6/-1)
tests/qmltests/Stages/tst_PhoneStage.qml (+4/-0)
tests/qmltests/Stages/tst_SessionContainer.qml (+14/-7)
tests/qmltests/Stages/tst_TabletStage.qml (+154/-0)
tests/qmltests/Tutorial/tst_Tutorial.qml (+286/-0)
tests/qmltests/tst_Shell.qml (+26/-5)
tests/qmltests/tst_TabletShell.qml (+2/-2)
tests/uqmlscene/ActiveFocusLogger.cpp (+48/-0)
tests/uqmlscene/ActiveFocusLogger.h (+37/-0)
tests/uqmlscene/CMakeLists.txt (+1/-0)
tests/uqmlscene/main.cpp (+13/-0)
To merge this branch: bzr merge lp:~mterry/unity8/tutorial-refactor
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve on 2015-02-11
Andrea Cimitan (community) 2014-10-28 Approve on 2015-01-09
Review via email: mp+239874@code.launchpad.net

This proposal has been superseded by a proposal from 2015-02-11.

Commit Message

Redesign tutorial to match latest spec (just removing obsolete pages and redesigning look, no new pages yet)

Description of the Change

Redesign tutorial to match latest spec (just removing obsolete pages and redesigning look, no new pages yet)

Specifically, both the greeter and top-edge pages are dropped. Design decided they were self-explanatory enough that they didn't need to be part of the tutorial (in an effort to make it shorter).

The tutorial is still missing right-edge and bottom-edge pages. But since those are new, I am going to do them as a separate branch. This branch is just about trimming the tutorial and redesigning the look and feel.

This leaves us with a very short tutorial right now, but I'm OK with that. It will get longer in a moment with my next tutorial branch.

I did a whole refactor of the tutorial code while here. I didn't like the old way I had done it (too procedural rather than declarative).

I've also taken this opportunity to sync our technical name for the tutorial (edge demo) to what Design calls it (tutorial).

Here is the spec from Design:
https://docs.google.com/a/canonical.com/presentation/d/1M8S1wTo-5lIFaIxxgls9TCEnvR79XThGEVpRtkHJY_A

== Checklist ==

 * Are there any related MPs required for this MP to build/function as expected? Please list.
 No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
 Yes

 * Did you make sure that your branch does not contain spurious tags?
 Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
 I'm in that team

 * If you changed the UI, has there been a design review?
 Yes-ish, they saw an earlier version, will get final approval.

To post a comment you must log in.
Albert Astals Cid (aacid) wrote :

Text conflict in qml/Shell.qml
Conflict adding file qml/Tutorial. Moved existing file to qml/Tutorial.moved.
Text conflict in qml/Tutorial/Tutorial.qml
3 conflicts encountered.

Albert Astals Cid (aacid) wrote :

Text conflict in qml/Shell.qml
1 conflicts encountered.

Albert Astals Cid (aacid) wrote :

I've triggered a rebuild, don't think the failures are because of this branch.

Albert Astals Cid (aacid) wrote :

Ha! That was bad timing. I've cancelled the rebuild :D

Albert Astals Cid (aacid) wrote :

And my cancelled build appears as failed :/

Andrea Cimitan (cimi) wrote :

I tested this few times in a row, until I found my launcher stuck 2px on screen when hidden (like always on screen 2px).
Might be some of the changes to Launcher.qml

another small thing, more a question, you added both chevron and tick png, but while for tick you used gridUnit, you used pixels for chevron... should both scale or not?

Michael Terry (mterry) wrote :

I'll look at the 2px-launcher problem.

As for gridUnit vs pixels... Arrow.qml mentions the size of the "chevron.png" source image in pixels, but for its actual height/width, it scales to whatever size the Arrow object is. So they both scale fine, I believe.

Michael Terry (mterry) wrote :

OK, fixed the launcher sometimes being left exposed. Thanks for the catch!

Andrea Cimitan (cimi) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?
y
 * Did CI run pass? If not, please explain why.
y
 * Did you make sure that the branch does not contain spurious tags?
y

thanks, no issues I noticed now!

review: Approve
Michael Terry (mterry) wrote :

I'm going to wait to top-approve this, because design is still doing some minor tweaks (color tweaks, size of text, etc)

lp:~mterry/unity8/tutorial-refactor updated on 2015-01-09
1395. By Michael Terry on 2015-01-09

Tweak colors of slider

lp:~mterry/unity8/tutorial-refactor updated on 2015-01-09
1396. By Michael Terry on 2015-01-09

Merge from trunk

lp:~mterry/unity8/tutorial-refactor updated on 2015-01-15
1397. By Michael Terry on 2015-01-15

Merge from trunk

lp:~mterry/unity8/tutorial-refactor updated on 2015-01-28
1398. By Michael Terry on 2015-01-28

Merge from trunk

lp:~mterry/unity8/tutorial-refactor updated on 2015-02-01
1399. By Michael Terry on 2015-02-01

Don't let user interact with launcher when not 'visible'

lp:~mterry/unity8/tutorial-refactor updated on 2015-02-03
1400. By Michael Terry on 2015-02-03

Update autopilot

Michael Terry (mterry) wrote :

OK, so this got updated with a couple minor design tweaks and a fix for the autopilot tests. Design has signed off on this version. And the autopilot passes now (well, you can't see it here yet -- I just kicked a rebuild, but you can see it in https://code.launchpad.net/~mterry/unity8/tutorial-new-screens/+merge/245699).

So I'm inclined to top-approve again if there isn't an objection.

lp:~mterry/unity8/tutorial-refactor updated on 2015-02-05
1401. By Michael Terry on 2015-02-05

Merge from trunk

1402. By Michael Terry on 2015-02-05

Fix tutorial qmluitests with the new lightdm mock

lp:~mterry/unity8/tutorial-refactor updated on 2015-02-11
1403. By Michael Terry on 2015-02-11

Rebase on lp:~dandrader/unity8/fixSurfaceActiveFocus

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8.install'
2--- debian/unity8.install 2014-11-14 22:40:00 +0000
3+++ debian/unity8.install 2015-02-11 16:31:26 +0000
4@@ -10,5 +10,6 @@
5 usr/share/unity8/Panel
6 usr/share/unity8/Shell.qml
7 usr/share/unity8/Stages
8+usr/share/unity8/Tutorial
9 usr/share/unity8/Wizard
10 usr/share/url-dispatcher/urls/unity8-dash.url-dispatcher
11
12=== modified file 'plugins/Ubuntu/Gestures/TouchGate.cpp'
13--- plugins/Ubuntu/Gestures/TouchGate.cpp 2014-10-17 11:01:53 +0000
14+++ plugins/Ubuntu/Gestures/TouchGate.cpp 2015-02-11 16:31:26 +0000
15@@ -53,6 +53,8 @@
16 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
17 m_touchInfoMap[touchPoint.id()].ended = false;
18 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
19+
20+ Q_EMIT pressed();
21 }
22
23 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
24
25=== modified file 'plugins/Ubuntu/Gestures/TouchGate.h'
26--- plugins/Ubuntu/Gestures/TouchGate.h 2014-10-17 11:01:53 +0000
27+++ plugins/Ubuntu/Gestures/TouchGate.h 2015-02-11 16:31:26 +0000
28@@ -53,6 +53,8 @@
29 Q_SIGNALS:
30 void targetItemChanged(QQuickItem *item);
31
32+ void pressed();
33+
34 protected:
35 void touchEvent(QTouchEvent *event) override;
36 private:
37
38=== modified file 'qml/CMakeLists.txt'
39--- qml/CMakeLists.txt 2014-11-14 23:30:10 +0000
40+++ qml/CMakeLists.txt 2015-02-11 16:31:26 +0000
41@@ -9,10 +9,11 @@
42 Dash
43 graphics
44 Greeter
45+ Launcher
46+ Notifications
47 Panel
48- Launcher
49 Stages
50- Notifications
51+ Tutorial
52 Wizard
53 )
54
55
56=== removed file 'qml/Components/EdgeDemo.qml'
57--- qml/Components/EdgeDemo.qml 2014-11-19 19:05:35 +0000
58+++ qml/Components/EdgeDemo.qml 1970-01-01 00:00:00 +0000
59@@ -1,254 +0,0 @@
60-/*
61- * Copyright (C) 2013 Canonical, Ltd.
62- *
63- * This program is free software; you can redistribute it and/or modify
64- * it under the terms of the GNU General Public License as published by
65- * the Free Software Foundation; version 3.
66- *
67- * This program is distributed in the hope that it will be useful,
68- * but WITHOUT ANY WARRANTY; without even the implied warranty of
69- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
70- * GNU General Public License for more details.
71- *
72- * You should have received a copy of the GNU General Public License
73- * along with this program. If not, see <http://www.gnu.org/licenses/>.
74- */
75-
76-import AccountsService 0.1
77-import LightDM 0.1 as LightDM
78-import QtQuick 2.0
79-import Ubuntu.Components 1.1
80-
81-Item {
82- id: demo
83-
84- property Item greeter
85- property Item launcher
86- property Item panel
87- property Item stages
88-
89- property bool launcherEnabled: true
90- property bool stagesEnabled: true
91- property bool panelEnabled: true
92- property bool panelContentEnabled: true
93- property bool running: !launcherEnabled || !stagesEnabled || !panelEnabled || !panelContentEnabled
94-
95- property bool paused: false
96-
97- onPausedChanged: {
98- if (d.rightEdgeDemo) d.rightEdgeDemo.paused = paused
99- if (d.topEdgeDemo) d.topEdgeDemo.paused = paused
100- if (d.bottomEdgeDemo) d.bottomEdgeDemo.paused = paused
101- if (d.leftEdgeDemo) d.leftEdgeDemo.paused = paused
102- if (d.finalEdgeDemo) d.finalEdgeDemo.paused = paused
103- }
104-
105- function hideEdgeDemoInShell() {
106- AccountsService.demoEdges = false;
107- stopDemo();
108- }
109-
110- function hideEdgeDemoInGreeter() {
111- // TODO: AccountsService.demoEdges = false as lightdm user
112- }
113-
114- function hideEdgeDemos() {
115- hideEdgeDemoInGreeter();
116- hideEdgeDemoInShell();
117- }
118-
119- function stopDemo() {
120- launcherEnabled = true
121- stagesEnabled = true
122- panelEnabled = true
123- panelContentEnabled = true
124-
125- // Use a tiny delay for these destroy() calls because if a lot is
126- // happening at once (like creating and being destroyed in same event
127- // loop, as might happen when answering a call while demo is open),
128- // the destroy() call will be ignored.
129- if (d.rightEdgeDemo) d.rightEdgeDemo.destroy(1);
130- if (d.topEdgeDemo) d.topEdgeDemo.destroy(1);
131- if (d.bottomEdgeDemo) d.bottomEdgeDemo.destroy(1);
132- if (d.leftEdgeDemo) d.leftEdgeDemo.destroy(1);
133- if (d.finalEdgeDemo) d.finalEdgeDemo.destroy(1);
134- }
135-
136- function startDemo() {
137- if (!d.overlay) {
138- d.overlay = Qt.createComponent("EdgeDemoOverlay.qml")
139- }
140-
141- launcherEnabled = false;
142- stagesEnabled = false;
143- panelEnabled = false;
144- panelContentEnabled = false;
145-
146- // Begin with either greeter or top, depending on which is visible
147- if (greeter && greeter.shown) {
148- startRightEdgeDemo()
149- } else {
150- startTopEdgeDemo()
151- }
152- }
153-
154- QtObject {
155- id: d
156- property Component overlay
157- property QtObject rightEdgeDemo
158- property QtObject topEdgeDemo
159- property QtObject bottomEdgeDemo
160- property QtObject leftEdgeDemo
161- property QtObject finalEdgeDemo
162- property bool showEdgeDemo: AccountsService.demoEdges
163- property bool showEdgeDemoInGreeter: AccountsService.demoEdges // TODO: AccountsService.demoEdges as lightdm user
164-
165- function restartDemo() {
166- stopDemo()
167- if (d.showEdgeDemo) {
168- startDemo()
169- }
170- }
171-
172- onShowEdgeDemoChanged: restartDemo()
173- }
174-
175- Connections {
176- target: i18n
177- onLanguageChanged: d.restartDemo()
178- }
179-
180- function startRightEdgeDemo() {
181- if (demo.greeter) {
182- d.rightEdgeDemo = d.overlay.createObject(demo.greeter, {
183- "edge": "right",
184- "title": i18n.tr("Right edge"),
185- "text": i18n.tr("Try swiping from the right edge to unlock the phone"),
186- "anchors.fill": demo.greeter,
187- });
188- }
189- if (d.rightEdgeDemo) {
190- d.rightEdgeDemo.onSkip.connect(demo.hideEdgeDemos)
191- } else {
192- stopDemo();
193- }
194- }
195-
196- Connections {
197- target: demo.greeter
198-
199- function hide() {
200- if (d.rightEdgeDemo && d.rightEdgeDemo.available) {
201- d.rightEdgeDemo.hide()
202- hideEdgeDemoInGreeter()
203- startTopEdgeDemo()
204- }
205- }
206-
207- onUnlocked: hide()
208- onShownChanged: if (!greeter.shown) hide()
209- }
210-
211- function startTopEdgeDemo() {
212- demo.panelEnabled = true;
213- if (demo.stages) {
214- d.topEdgeDemo = d.overlay.createObject(demo.panel, {
215- "edge": "top",
216- "title": i18n.tr("Top edge"),
217- "text": i18n.tr("Try swiping from the top edge to access the indicators"),
218- "anchors.fill": demo.panel,
219- });
220- }
221- if (d.topEdgeDemo) {
222- d.topEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
223- } else {
224- stopDemo();
225- }
226- }
227-
228- Connections {
229- target: demo.panel.indicators
230- onFullyOpenedChanged: {
231- if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
232- d.topEdgeDemo.hideNow()
233- startBottomEdgeDemo()
234- }
235- }
236- }
237-
238- function startBottomEdgeDemo() {
239- if (demo.panel.indicators) {
240- d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
241- "edge": "bottom",
242- "title": i18n.tr("Close"),
243- "text": i18n.tr("Swipe up again to close the settings screen"),
244- "anchors.fill": demo.panel.indicators,
245- });
246- }
247- if (d.bottomEdgeDemo) {
248- d.bottomEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
249- } else {
250- stopDemo();
251- }
252- }
253-
254- Connections {
255- target: demo.panel.indicators
256- onPartiallyOpenedChanged: {
257- if (d.bottomEdgeDemo &&
258- d.bottomEdgeDemo.available &&
259- !demo.panel.indicators.partiallyOpened &&
260- !demo.panel.indicators.fullyOpened) {
261- d.bottomEdgeDemo.hideNow()
262- startLeftEdgeDemo()
263- }
264- }
265- }
266-
267- function startLeftEdgeDemo() {
268- demo.panelEnabled = false;
269- demo.launcherEnabled = true;
270- if (demo.stages) {
271- d.leftEdgeDemo = d.overlay.createObject(demo.stages, {
272- "edge": "left",
273- "title": i18n.tr("Left edge"),
274- "text": i18n.tr("Swipe from the left to reveal the launcher for quick access to apps"),
275- "anchors.fill": demo.stages,
276- });
277- }
278- if (d.leftEdgeDemo) {
279- d.leftEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
280- } else {
281- stopDemo();
282- }
283- }
284-
285- Connections {
286- target: demo.launcher
287- onStateChanged: {
288- if (d.leftEdgeDemo && d.leftEdgeDemo.available && launcher.state == "visible") {
289- d.leftEdgeDemo.hide()
290- launcher.hide()
291- startFinalEdgeDemo()
292- }
293- }
294- }
295-
296- function startFinalEdgeDemo() {
297- demo.launcherEnabled = false;
298- if (demo.stages) {
299- d.finalEdgeDemo = d.overlay.createObject(demo.stages, {
300- "edge": "none",
301- "title": i18n.tr("Well done"),
302- "text": i18n.tr("You have now mastered the edge gestures and can start using the phone<br><br>Tap on the screen to start"),
303- "anchors.fill": demo.stages,
304- "showSkip": false,
305- });
306- }
307- if (d.finalEdgeDemo) {
308- d.finalEdgeDemo.onSkip.connect(demo.hideEdgeDemoInShell)
309- } else {
310- stopDemo();
311- }
312- }
313-}
314
315=== removed file 'qml/Components/EdgeDemoOverlay.qml'
316--- qml/Components/EdgeDemoOverlay.qml 2014-07-01 23:45:28 +0000
317+++ qml/Components/EdgeDemoOverlay.qml 1970-01-01 00:00:00 +0000
318@@ -1,273 +0,0 @@
319-/*
320- * Copyright (C) 2013 Canonical, Ltd.
321- *
322- * This program is free software; you can redistribute it and/or modify
323- * it under the terms of the GNU General Public License as published by
324- * the Free Software Foundation; version 3.
325- *
326- * This program is distributed in the hope that it will be useful,
327- * but WITHOUT ANY WARRANTY; without even the implied warranty of
328- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
329- * GNU General Public License for more details.
330- *
331- * You should have received a copy of the GNU General Public License
332- * along with this program. If not, see <http://www.gnu.org/licenses/>.
333- */
334-
335-import Powerd 0.1
336-import QtQuick 2.0
337-import QtGraphicalEffects 1.0
338-import Unity.Application 0.1
339-import Ubuntu.Components 0.1
340-
341-Showable {
342- id: overlay
343-
344- /*
345- * Valid values are "left", "right", "top", "bottom", or "none".
346- */
347- property string edge: "top"
348-
349- /*
350- * This is the header displayed, like "Right edge".
351- */
352- property alias title: titleLabel.text
353-
354- /*
355- * This is the block of text displayed below the header.
356- */
357- property alias text: textLabel.text
358-
359- /*
360- * This is the text for the skip button.
361- */
362- property alias skipText: skipLabel.text
363-
364- /*
365- * This is the visible status of the skip button.
366- */
367- property alias showSkip: skipLabel.visible
368-
369- /*
370- * Whether this demo is running currently.
371- */
372- readonly property bool active: available && visible
373-
374- /*
375- * Whether animations are paused.
376- */
377- property alias paused: wholeAnimation.paused
378-
379- /*
380- * Whether animations are running.
381- */
382- readonly property alias running: wholeAnimation.running
383-
384- signal skip()
385-
386- function doSkip() {
387- d.skipOnHide = true;
388- hide();
389- }
390-
391- function hideNow() {
392- overlay.visible = false;
393- overlay.available = false;
394- if (d.skipOnHide) {
395- overlay.skip();
396- }
397- }
398-
399- showAnimation: StandardAnimation {
400- property: "opacity"
401- to: 1
402- onRunningChanged: if (running) overlay.visible = true
403- }
404- hideAnimation: StandardAnimation {
405- property: "opacity"
406- to: 0
407- duration: UbuntuAnimation.BriskDuration
408- onRunningChanged: if (!running) overlay.hideNow()
409- }
410-
411- QtObject {
412- id: d
413- property bool skipOnHide: false
414- property int edgeMargin: units.gu(4)
415- }
416-
417- Rectangle {
418- objectName: "backgroundShade"
419-
420- anchors.fill: parent
421- color: "black"
422- opacity: 0.8
423- visible: overlay.active
424-
425- MouseArea {
426- objectName: "backgroundShadeMouseArea"
427-
428- anchors.fill: parent
429- enabled: overlay.edge == "none" && overlay.opacity == 1.0
430- onClicked: overlay.doSkip()
431- }
432- }
433-
434- Item {
435- id: hintGroup
436- x: 0
437- y: 0
438- width: parent.width
439- height: parent.height
440- visible: overlay.active
441-
442- Column {
443- id: labelGroup
444- spacing: units.gu(3)
445-
446- anchors {
447- margins: d.edgeMargin
448- left: parent.left
449- top: overlay.edge == "bottom" ? undefined : parent.top
450- bottom: overlay.edge == "bottom" ? parent.bottom : undefined
451- }
452-
453- Label {
454- id: titleLabel
455- fontSize: "x-large"
456- width: units.gu(25)
457- wrapMode: Text.WordWrap
458- }
459-
460- Label {
461- id: textLabel
462- width: units.gu(25)
463- wrapMode: Text.WordWrap
464- }
465-
466- Label {
467- id: skipLabel
468- objectName: "skipLabel"
469- text: i18n.tr("Skip intro")
470- color: UbuntuColors.orange
471- fontSize: "small"
472-
473- Icon {
474- anchors.left: parent.right
475- anchors.verticalCenter: parent.verticalCenter
476- height: units.dp(12)
477- width: units.dp(12)
478- name: "chevron"
479- color: UbuntuColors.orange
480- }
481-
482- MouseArea {
483- // Make clickable area bigger than just the link because
484- // otherwise, the edge demo will feel hard to dismiss.
485- anchors.fill: parent
486- anchors.margins: -units.gu(5)
487- onClicked: overlay.doSkip()
488- }
489- }
490- }
491-
492- LinearGradient {
493- id: edgeHint
494- property int size: 1
495- cached: false
496- visible: overlay.edge != "none"
497- gradient: Gradient {
498- GradientStop {
499- position: 0.0
500- color: Qt.hsla(16.0/360.0, 0.83, 0.47, 0.4) // UbuntuColors.orange, but transparent
501- }
502- GradientStop {
503- position: 1.0
504- color: "transparent"
505- }
506- }
507- anchors.fill: parent
508- start: {
509- if (overlay.edge == "right") {
510- return Qt.point(width, 0);
511- } else if (overlay.edge == "left") {
512- return Qt.point(0, 0);
513- } else if (overlay.edge == "top") {
514- return Qt.point(0, 0);
515- } else {
516- return Qt.point(0, height);
517- }
518- }
519- end: {
520- if (overlay.edge == "right") {
521- return Qt.point(width - size, 0);
522- } else if (overlay.edge == "left") {
523- return Qt.point(size, 0);
524- } else if (overlay.edge == "top") {
525- return Qt.point(0, size);
526- } else {
527- return Qt.point(0, height - size);
528- }
529- }
530- }
531- }
532-
533- SequentialAnimation {
534- id: wholeAnimation
535- objectName: "wholeAnimation"
536- running: overlay.active
537-
538- ParallelAnimation {
539- id: fadeInAnimation
540-
541- StandardAnimation {
542- target: labelGroup
543- property: {
544- if (overlay.edge == "right" || overlay.edge == "left") {
545- return "anchors.leftMargin";
546- } else if (overlay.edge == "bottom") {
547- return "anchors.bottomMargin";
548- } else {
549- return "anchors.topMargin";
550- }
551- }
552- from: {
553- if (overlay.edge == "right") {
554- return d.edgeMargin + units.gu(3)
555- } else {
556- return d.edgeMargin - units.gu(3)
557- }
558- }
559- to: d.edgeMargin
560- duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
561- }
562- StandardAnimation {
563- target: overlay
564- property: "opacity"
565- from: 0.0
566- to: 1.0
567- duration: UbuntuAnimation.SleepyDuration
568- }
569- }
570-
571- SequentialAnimation {
572- id: hintAnimation
573- loops: Animation.Infinite
574- property string prop: (overlay.edge == "left" || overlay.edge == "right") ? "x" : "y"
575- property double endVal: units.dp(5) * ((overlay.edge == "left" || overlay.edge == "top") ? 1 : -1)
576- property double maxGlow: units.dp(20)
577- property int duration: overlay.edge == "none" ? 0 : UbuntuAnimation.SleepyDuration
578-
579- ParallelAnimation {
580- StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: 0; to: hintAnimation.endVal; duration: hintAnimation.duration }
581- StandardAnimation { target: edgeHint; property: "size"; from: 1; to: hintAnimation.maxGlow; duration: hintAnimation.duration }
582- }
583-
584- // Undo the above
585- ParallelAnimation {
586- StandardAnimation { target: hintGroup; property: hintAnimation.prop; from: hintAnimation.endVal; to: 0; duration: hintAnimation.duration }
587- StandardAnimation { target: edgeHint; property: "size"; from: hintAnimation.maxGlow; to: 1; duration: hintAnimation.duration }
588- }
589- }
590- }
591-}
592
593=== modified file 'qml/Launcher/Launcher.qml'
594--- qml/Launcher/Launcher.qml 2014-12-11 13:24:26 +0000
595+++ qml/Launcher/Launcher.qml 2015-02-11 16:31:26 +0000
596@@ -26,6 +26,7 @@
597 property bool autohideEnabled: false
598 property bool available: true // can be used to disable all interactions
599 property alias inverted: panel.inverted
600+ property bool shadeBackground: true // can be used to disable background shade when launcher is visible
601
602 property int panelWidth: units.gu(8)
603 property int dragAreaWidth: units.gu(1)
604@@ -33,6 +34,10 @@
605 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
606 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
607
608+ readonly property bool dragging: dragArea.dragging
609+ readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
610+ readonly property real visibleWidth: panel.width + panel.x
611+
612 readonly property bool shown: panel.x > -panel.width
613
614 // emitted when an application is selected
615@@ -154,7 +159,7 @@
616
617 MouseArea {
618 id: launcherDragArea
619- enabled: root.state == "visible"
620+ enabled: root.available && root.state == "visible"
621 anchors.fill: panel
622 anchors.rightMargin: -units.gu(2)
623 drag {
624@@ -180,7 +185,7 @@
625 right: parent.right
626 bottom: parent.bottom
627 }
628- enabled: root.state == "visible"
629+ enabled: root.shadeBackground && root.state == "visible"
630 onPressed: {
631 root.state = ""
632 }
633@@ -190,7 +195,7 @@
634 id: backgroundShade
635 anchors.fill: parent
636 color: "black"
637- opacity: root.state == "visible" ? 0.6 : 0
638+ opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
639
640 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
641 }
642@@ -198,14 +203,14 @@
643 LauncherPanel {
644 id: panel
645 objectName: "launcherPanel"
646- enabled: root.available
647+ enabled: root.available && root.state == "visible"
648 width: root.panelWidth
649 anchors {
650 top: parent.top
651 bottom: parent.bottom
652 }
653 x: -width
654- visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
655+ visible: root.x > 0 || x > -width || dragArea.status === DirectionalDragArea.Undecided
656 model: LauncherModel
657
658 property bool animate: true
659@@ -247,6 +252,7 @@
660 direction: Direction.Rightwards
661
662 enabled: root.available
663+ x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
664 width: root.dragAreaWidth
665 height: root.height
666
667@@ -260,7 +266,7 @@
668 // would appear right next to the user's finger out of nowhere.
669 // Instead, we make the panel go towards the user's finger in several
670 // steps. ie., in an animated way.
671- var targetPanelX = Math.min(0, touchX - panel.width)
672+ var targetPanelX = Math.min(0, touchX - panel.width) - root.x
673 var delta = targetPanelX - panel.x
674 // the trick is not to go all the way (1.0) as it would cause a sudden jump
675 panel.x += 0.4 * delta
676@@ -292,7 +298,7 @@
677 name: "visible"
678 PropertyChanges {
679 target: panel
680- x: 0
681+ x: -root.x // so we never go past panelWidth, even when teased by tutorial
682 }
683 },
684 State {
685
686=== modified file 'qml/Shell.qml'
687--- qml/Shell.qml 2015-02-05 10:29:34 +0000
688+++ qml/Shell.qml 2015-02-11 16:31:26 +0000
689@@ -36,6 +36,7 @@
690 import "Components"
691 import "Notifications"
692 import "Stages"
693+import "Tutorial"
694 import "Wizard"
695 import Unity.Notifications 1.0 as NotificationBackend
696 import Unity.Session 0.1
697@@ -62,7 +63,7 @@
698
699 readonly property bool locked: LightDM.Greeter.active && !LightDM.Greeter.authenticated && !forcedUnlock
700 readonly property alias hasLockedApp: greeter.hasLockedApp
701- readonly property bool forcedUnlock: edgeDemo.running
702+ readonly property bool forcedUnlock: tutorial.running
703 onForcedUnlockChanged: if (forcedUnlock) lockscreen.hide()
704
705 property bool sideStageEnabled: shell.width >= units.gu(100)
706@@ -161,7 +162,7 @@
707
708 ScreenGrabber {
709 id: screenGrabber
710- z: edgeDemo.z + 10
711+ z: dialogs.z + 10
712 enabled: Powerd.status === Powerd.On
713 }
714
715@@ -252,14 +253,16 @@
716 }
717
718 onApplicationAdded: {
719- if (greeter.shown && appId != "unity8-dash") {
720- greeter.startUnlock()
721+ if (appId != "unity8-dash") {
722+ if (greeter.shown) {
723+ greeter.startUnlock();
724+ }
725
726 // If this happens on first boot, we may be in edge
727 // tutorial or wizard while receiving a call. But a call
728 // is more important than wizard so just bail out of those.
729- if (edgeDemo.running) {
730- edgeDemo.hideEdgeDemos();
731+ if (tutorial.running) {
732+ tutorial.finish();
733 wizard.hide();
734 }
735 }
736@@ -287,6 +290,15 @@
737 source: usageModeSettings.usageMode === "Windowed" ? "Stages/DesktopStage.qml"
738 : tabletMode ? "Stages/TabletStage.qml" : "Stages/PhoneStage.qml"
739
740+ property bool interactive: tutorial.stagesEnabled
741+ && !greeter.shown
742+ && !lockscreen.shown
743+ && panel.indicators.fullyClosed
744+ && launcher.progress == 0
745+ && !notifications.useModal
746+
747+ onInteractiveChanged: { if (interactive) { focus = true; } }
748+
749 Binding {
750 target: applicationsDisplayLoader.item
751 property: "objectName"
752@@ -306,12 +318,12 @@
753 Binding {
754 target: applicationsDisplayLoader.item
755 property: "interactive"
756- value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
757+ value: applicationsDisplayLoader.interactive
758 }
759 Binding {
760 target: applicationsDisplayLoader.item
761 property: "spreadEnabled"
762- value: edgeDemo.stagesEnabled && !greeter.hasLockedApp
763+ value: tutorial.stagesEnabled && !greeter.hasLockedApp
764 }
765 Binding {
766 target: applicationsDisplayLoader.item
767@@ -644,9 +656,13 @@
768 LauncherModel.setUser(user);
769 }
770
771- onTapped: launcher.tease()
772+ onTapped: {
773+ if (!tutorial.running) {
774+ launcher.tease();
775+ }
776+ }
777 onDraggingChanged: {
778- if (dragging) {
779+ if (dragging && !tutorial.running) {
780 launcher.tease();
781 }
782 }
783@@ -684,7 +700,7 @@
784
785 onStatusChanged: {
786 if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
787- !callManager.hasCalls && !edgeDemo.running) {
788+ !callManager.hasCalls && !tutorial.running) {
789 // We don't want to simply call greeter.showNow() here, because
790 // that will take too long. Qt will delay button event
791 // handling until the greeter is done loading and may think the
792@@ -700,7 +716,7 @@
793 }
794
795 function showHome() {
796- if (edgeDemo.running) {
797+ if (tutorial.running) {
798 return
799 }
800
801@@ -742,8 +758,8 @@
802 anchors.fill: parent //because this draws indicator menus
803 indicators {
804 hides: [launcher]
805- available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
806- contentEnabled: edgeDemo.panelContentEnabled
807+ available: tutorial.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
808+ contentEnabled: tutorial.panelContentEnabled
809 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
810
811 minimizedPanelHeight: units.gu(3)
812@@ -776,8 +792,9 @@
813 anchors.bottom: parent.bottom
814 width: parent.width
815 dragAreaWidth: shell.edgeSize
816- available: edgeDemo.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
817+ available: tutorial.launcherEnabled && (!shell.locked || AccountsService.enableLauncherWhileLocked) && !greeter.hasLockedApp
818 inverted: usageModeSettings.usageMode === "Staged"
819+ shadeBackground: !tutorial.running
820
821 onShowDashHome: showHome()
822 onDash: showDash()
823@@ -790,7 +807,7 @@
824 if (greeter.hasLockedApp) {
825 greeter.startUnlock()
826 }
827- if (!edgeDemo.running)
828+ if (!tutorial.running)
829 shell.activateApplication(appId)
830 }
831 onShownChanged: {
832@@ -863,15 +880,17 @@
833 }
834 }
835
836- EdgeDemo {
837- id: edgeDemo
838- objectName: "edgeDemo"
839- z: dialogs.z + 10
840- paused: Powerd.status === Powerd.Off || wizard.active // Saves power
841- greeter: greeter
842+ Tutorial {
843+ id: tutorial
844+ objectName: "tutorial"
845+ active: AccountsService.demoEdges
846+ paused: LightDM.Greeter.active
847 launcher: launcher
848 panel: panel
849 stages: stages
850+ overlay: overlay
851+
852+ onFinished: AccountsService.demoEdges = false
853 }
854
855 Connections {
856@@ -881,7 +900,7 @@
857
858 Rectangle {
859 id: shutdownFadeOutRectangle
860- z: edgeDemo.z + 10
861+ z: screenGrabber.z + 10
862 enabled: false
863 visible: false
864 color: "black"
865
866=== modified file 'qml/Stages/ApplicationWindow.qml'
867--- qml/Stages/ApplicationWindow.qml 2014-11-04 14:51:13 +0000
868+++ qml/Stages/ApplicationWindow.qml 2015-02-11 16:31:26 +0000
869@@ -18,7 +18,7 @@
870 import Ubuntu.Components 1.1
871 import Unity.Application 0.1
872
873-Item {
874+FocusScope {
875 id: root
876
877 // to be read from outside
878@@ -120,6 +120,8 @@
879 d.surfaceInitialized = false;
880 }
881 }
882+
883+ focus: true
884 }
885
886 StateGroup {
887
888=== modified file 'qml/Stages/PhoneStage.qml'
889--- qml/Stages/PhoneStage.qml 2015-01-12 11:21:17 +0000
890+++ qml/Stages/PhoneStage.qml 2015-02-11 16:31:26 +0000
891@@ -109,6 +109,8 @@
892
893 onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
894
895+ onFocusedAppDelegateChanged: focusedAppDelegate.focus = true;
896+
897 function indexOf(appId) {
898 for (var i = 0; i < ApplicationManager.count; i++) {
899 if (ApplicationManager.get(i).appId == appId) {
900
901=== modified file 'qml/Stages/SessionContainer.qml'
902--- qml/Stages/SessionContainer.qml 2014-11-12 01:09:03 +0000
903+++ qml/Stages/SessionContainer.qml 2015-02-11 16:31:26 +0000
904@@ -17,7 +17,7 @@
905 import QtQuick 2.0
906 import "Animations"
907
908-Item {
909+FocusScope {
910 id: root
911 objectName: "sessionContainer"
912 property QtObject session
913@@ -34,8 +34,8 @@
914 orientation: root.orientation
915 }
916
917-
918 Repeater {
919+ id: childSessionsRepeater
920 model: root.childSessions
921
922 delegate: Loader {
923@@ -45,9 +45,17 @@
924 // Only way to do recursive qml items.
925 source: Qt.resolvedUrl("SessionContainer.qml")
926
927- Binding {
928- target: item; when: item
929- property: "interactive"; value: root.interactive
930+ z: index
931+
932+ // Since a Loader is a FocusScope, propagate its focus to the loaded Item
933+ Binding {
934+ target: item; when: item
935+ property: "focus"; value: focus
936+ }
937+
938+ Binding {
939+ target: item; when: item
940+ property: "interactive"; value: index == (childSessionsRepeater.count - 1) && root.interactive
941 }
942
943 Binding {
944@@ -69,10 +77,6 @@
945 target: item; when: item
946 property: "orientation"; value: root.orientation
947 }
948-
949- Component.onDestruction: {
950- root.session.surface.forceActiveFocus();
951- }
952 }
953 }
954
955@@ -138,5 +142,16 @@
956 QtObject {
957 id: d
958 property var animations: []
959+
960+ property var focusedChild: {
961+ if (childSessionsRepeater.count == 0) {
962+ return _surfaceContainer;
963+ } else {
964+ return childSessionsRepeater.itemAt(childSessionsRepeater.count - 1);
965+ }
966+ }
967+ onFocusedChildChanged: {
968+ focusedChild.focus = true;
969+ }
970 }
971 }
972
973=== modified file 'qml/Stages/SpreadDelegate.qml'
974--- qml/Stages/SpreadDelegate.qml 2014-11-27 11:13:08 +0000
975+++ qml/Stages/SpreadDelegate.qml 2015-02-11 16:31:26 +0000
976@@ -21,7 +21,7 @@
977 import Ubuntu.Components 1.1
978 import "../Components"
979
980-Item {
981+FocusScope {
982 id: root
983
984 // to be read from outside
985@@ -65,6 +65,7 @@
986 ApplicationWindow {
987 id: appWindow
988 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
989+ focus: true
990 anchors {
991 fill: parent
992 topMargin: appWindow.fullscreen ? 0 : maximizedAppTopMargin
993
994=== modified file 'qml/Stages/SurfaceContainer.qml'
995--- qml/Stages/SurfaceContainer.qml 2014-11-04 14:49:47 +0000
996+++ qml/Stages/SurfaceContainer.qml 2015-02-11 16:31:26 +0000
997@@ -18,7 +18,7 @@
998 import Ubuntu.Components 1.1
999 import Ubuntu.Gestures 0.1 // For TouchGate
1000
1001-Item {
1002+FocusScope {
1003 id: root
1004 objectName: "surfaceContainer"
1005 property Item surface: null
1006@@ -26,10 +26,15 @@
1007 property int orientation
1008 property bool interactive
1009
1010+ signal surfacePressed()
1011+
1012 onSurfaceChanged: {
1013 if (surface) {
1014+ // Set the surface focus *after* it is added to the scene to
1015+ // ensure an update to the scene's active focus.
1016+ surface.focus = false;
1017 surface.parent = root;
1018- d.forceSurfaceActiveFocusIfReady();
1019+ surface.focus = true;
1020 } else {
1021 hadSurface = true;
1022 }
1023@@ -38,7 +43,6 @@
1024 Binding { target: surface; property: "orientation"; value: root.orientation }
1025 Binding { target: surface; property: "z"; value: 1 }
1026 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
1027- Binding { target: surface; property: "focus"; value: root.interactive; when: surface }
1028 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
1029
1030 TouchGate {
1031@@ -46,28 +50,10 @@
1032 anchors.fill: root
1033 enabled: root.surface ? root.surface.enabled : false
1034 z: 2
1035- }
1036-
1037- Connections {
1038- target: root.surface
1039- // FIXME: I would rather not need to do this, but currently it doesn't get
1040- // active focus without it and I don't know why.
1041- // Possibly because if an item get focus=true before it has a parent, once
1042- // it gets a parent QQuickWindow won't check its focus and update its activeFocus
1043- // accordingly. Unlike when you focus=true after the item already has a parent.
1044- onFocusChanged: d.forceSurfaceActiveFocusIfReady();
1045- onParentChanged: d.forceSurfaceActiveFocusIfReady();
1046- onEnabledChanged: d.forceSurfaceActiveFocusIfReady();
1047- }
1048-
1049- QtObject {
1050- id: d
1051- function forceSurfaceActiveFocusIfReady() {
1052- if (root.surface !== null &&
1053- root.surface.focus &&
1054- root.surface.parent === root &&
1055- root.surface.enabled) {
1056- root.surface.forceActiveFocus();
1057+ onPressed: {
1058+ root.focus = true;
1059+ if (root.interactive) {
1060+ root.forceActiveFocus();
1061 }
1062 }
1063 }
1064
1065=== modified file 'qml/Stages/TabletStage.qml'
1066--- qml/Stages/TabletStage.qml 2015-01-12 11:21:17 +0000
1067+++ qml/Stages/TabletStage.qml 2015-02-11 16:31:26 +0000
1068@@ -76,6 +76,20 @@
1069
1070 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
1071 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
1072+
1073+ // Update the QML focus accordingly
1074+ updateSpreadDelegateFocus();
1075+ }
1076+
1077+ function updateSpreadDelegateFocus() {
1078+ if (priv.focusedAppId) {
1079+ var focusedAppIndex = priv.indexOf(priv.focusedAppId);
1080+ if (focusedAppIndex !== -1) {
1081+ spreadRepeater.itemAt(focusedAppIndex).focus = true;
1082+ } else {
1083+ console.warn("TabletStage: Failed to find the SpreadDelegate for appID=" + priv.focusedAppId);
1084+ }
1085+ }
1086 }
1087
1088 function indexOf(appId) {
1089
1090=== modified file 'qml/Stages/TransformedTabletSpreadDelegate.qml'
1091--- qml/Stages/TransformedTabletSpreadDelegate.qml 2014-09-02 12:57:15 +0000
1092+++ qml/Stages/TransformedTabletSpreadDelegate.qml 2015-02-11 16:31:26 +0000
1093@@ -366,7 +366,7 @@
1094 Scale {
1095 origin { x: 0; y: (spreadView.height * priv.scale) + maximizedAppTopMargin * 3 }
1096 xScale: 1
1097- yScale: isFullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
1098+ yScale: fullscreen ? 1 - priv.topMarginProgress * maximizedAppTopMargin / spreadView.height : 1
1099 },
1100 Translate {
1101 x: priv.xTranslate
1102
1103=== added directory 'qml/Tutorial'
1104=== added file 'qml/Tutorial/Arrow.qml'
1105--- qml/Tutorial/Arrow.qml 1970-01-01 00:00:00 +0000
1106+++ qml/Tutorial/Arrow.qml 2015-02-11 16:31:26 +0000
1107@@ -0,0 +1,56 @@
1108+/*
1109+ * Copyright (C) 2014 Canonical, Ltd.
1110+ *
1111+ * This program is free software; you can redistribute it and/or modify
1112+ * it under the terms of the GNU General Public License as published by
1113+ * the Free Software Foundation; version 3.
1114+ *
1115+ * This program is distributed in the hope that it will be useful,
1116+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1117+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1118+ * GNU General Public License for more details.
1119+ *
1120+ * You should have received a copy of the GNU General Public License
1121+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1122+ */
1123+
1124+import QtQuick 2.3
1125+import Ubuntu.Components 1.1
1126+
1127+Item {
1128+ id: root
1129+
1130+ property alias color: circle.color
1131+
1132+ // Will make whole arrow darker
1133+ property real darkenBy: 0
1134+
1135+ property alias chevronOpacity: chevron.opacity
1136+
1137+ ////
1138+
1139+ Rectangle {
1140+ id: circle
1141+ anchors.fill: parent
1142+ radius: width / 2
1143+ }
1144+
1145+ Image {
1146+ id: chevron
1147+ anchors.centerIn: parent
1148+ source: Qt.resolvedUrl("graphics/chevron.png")
1149+ fillMode: Image.PreserveAspectFit
1150+ sourceSize.width: 152
1151+ sourceSize.height: 152
1152+ width: parent.width / 2
1153+ height: parent.height / 2
1154+ }
1155+
1156+ Rectangle {
1157+ id: darkCircle
1158+ anchors.fill: parent
1159+ radius: width / 2
1160+ color: "black"
1161+ opacity: root.darkenBy
1162+ }
1163+}
1164
1165=== added file 'qml/Tutorial/Slider.qml'
1166--- qml/Tutorial/Slider.qml 1970-01-01 00:00:00 +0000
1167+++ qml/Tutorial/Slider.qml 2015-02-11 16:31:26 +0000
1168@@ -0,0 +1,117 @@
1169+/*
1170+ * Copyright (C) 2014 Canonical, Ltd.
1171+ *
1172+ * This program is free software; you can redistribute it and/or modify
1173+ * it under the terms of the GNU General Public License as published by
1174+ * the Free Software Foundation; version 3.
1175+ *
1176+ * This program is distributed in the hope that it will be useful,
1177+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1178+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1179+ * GNU General Public License for more details.
1180+ *
1181+ * You should have received a copy of the GNU General Public License
1182+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1183+ */
1184+
1185+import QtQuick 2.3
1186+import Ubuntu.Components 1.1
1187+
1188+Item {
1189+ id: root
1190+
1191+ // Whether this slider is short or long
1192+ property bool shortSwipe
1193+
1194+ // How far the user has slid
1195+ property real offset
1196+
1197+ // Set to true when slider is being used
1198+ property bool active
1199+
1200+ // How far in percentage terms
1201+ readonly property real percent: d.slideOffset / target.x
1202+
1203+ QtObject {
1204+ id: d
1205+ readonly property color trayColor: "#424141"
1206+ readonly property real margin: units.gu(0.5)
1207+ readonly property real arrowSize: root.height - margin * 2
1208+ readonly property real dotSize: units.dp(1)
1209+ readonly property real slideOffset: Math.min(root.offset - offscreenOffset, target.x)
1210+ readonly property real offscreenOffset: units.gu(2)
1211+ }
1212+
1213+ implicitWidth: shortSwipe ? units.gu(15) : units.gu(27.5)
1214+ implicitHeight: units.gu(6.5)
1215+
1216+ Rectangle {
1217+ color: d.trayColor
1218+ anchors.fill: parent
1219+ anchors.rightMargin: clipBox.width - 1
1220+ }
1221+
1222+ // We want to have a circular border around the target. But we can't just
1223+ // do a radius on two of a rectangle's corners. So we clip a full circle.
1224+ Item {
1225+ id: clipBox
1226+
1227+ clip: true
1228+ anchors.top: parent.top
1229+ anchors.bottom: parent.bottom
1230+ anchors.right: parent.right
1231+ width: parent.height / 2
1232+
1233+ Rectangle {
1234+ color: d.trayColor
1235+ anchors.top: parent.top
1236+ anchors.bottom: parent.bottom
1237+ anchors.right: parent.right
1238+ width: parent.width * 2
1239+ radius: parent.width
1240+ }
1241+ }
1242+
1243+ Arrow {
1244+ id: target
1245+ width: d.arrowSize
1246+ height: d.arrowSize
1247+ color: "#73000000"
1248+ chevronOpacity: 0.52
1249+ anchors.right: parent.right
1250+ anchors.rightMargin: d.margin
1251+ anchors.verticalCenter: parent.verticalCenter
1252+ }
1253+
1254+ Row {
1255+ anchors.left: handle.horizontalCenter
1256+ anchors.right: target.horizontalCenter
1257+ anchors.verticalCenter: parent.verticalCenter
1258+
1259+ layoutDirection: Qt.RightToLeft
1260+ spacing: d.dotSize * 2
1261+
1262+ Repeater {
1263+ model: parent.width / (parent.spacing + d.dotSize)
1264+ Rectangle {
1265+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
1266+ height: d.dotSize
1267+ width: height
1268+ radius: width
1269+ color: "white"
1270+ opacity: 0.2
1271+ }
1272+ }
1273+ }
1274+
1275+ Arrow {
1276+ id: handle
1277+ width: d.arrowSize
1278+ height: d.arrowSize
1279+ color: UbuntuColors.orange
1280+ darkenBy: root.active ? 0.5 : 0
1281+ anchors.left: parent.left
1282+ anchors.leftMargin: d.slideOffset
1283+ anchors.verticalCenter: parent.verticalCenter
1284+ }
1285+}
1286
1287=== added file 'qml/Tutorial/Tutorial.qml'
1288--- qml/Tutorial/Tutorial.qml 1970-01-01 00:00:00 +0000
1289+++ qml/Tutorial/Tutorial.qml 2015-02-11 16:31:26 +0000
1290@@ -0,0 +1,85 @@
1291+/*
1292+ * Copyright (C) 2014 Canonical, Ltd.
1293+ *
1294+ * This program is free software; you can redistribute it and/or modify
1295+ * it under the terms of the GNU General Public License as published by
1296+ * the Free Software Foundation; version 3.
1297+ *
1298+ * This program is distributed in the hope that it will be useful,
1299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1301+ * GNU General Public License for more details.
1302+ *
1303+ * You should have received a copy of the GNU General Public License
1304+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1305+ */
1306+
1307+import QtQuick 2.3
1308+import Ubuntu.Components 1.1
1309+
1310+Item {
1311+ id: root
1312+
1313+ property alias active: loader.active
1314+ property bool paused
1315+
1316+ property Item launcher
1317+ property Item panel
1318+ property Item stages
1319+ property Item overlay
1320+
1321+ readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
1322+ readonly property bool stagesEnabled: loader.item ? loader.item.stagesEnabled : true
1323+ readonly property bool panelEnabled: loader.item ? loader.item.panelEnabled : true
1324+ readonly property bool panelContentEnabled: loader.item ? loader.item.panelContentEnabled : true
1325+ readonly property bool running: loader.item ? loader.item.running : false
1326+
1327+ function finish() {
1328+ if (loader.item) {
1329+ loader.item.finish();
1330+ }
1331+ }
1332+
1333+ signal finished()
1334+
1335+ Loader {
1336+ id: loader
1337+ anchors.fill: parent
1338+ source: "TutorialContent.qml"
1339+
1340+ Binding {
1341+ target: loader.item
1342+ property: "paused"
1343+ value: root.paused
1344+ }
1345+
1346+ Binding {
1347+ target: loader.item
1348+ property: "launcher"
1349+ value: root.launcher
1350+ }
1351+
1352+ Binding {
1353+ target: loader.item
1354+ property: "panel"
1355+ value: root.panel
1356+ }
1357+
1358+ Binding {
1359+ target: loader.item
1360+ property: "stages"
1361+ value: root.stages
1362+ }
1363+
1364+ Binding {
1365+ target: loader.item
1366+ property: "overlay"
1367+ value: root.overlay
1368+ }
1369+
1370+ Connections {
1371+ target: loader.item
1372+ onFinished: root.finished()
1373+ }
1374+ }
1375+}
1376
1377=== added file 'qml/Tutorial/TutorialContent.qml'
1378--- qml/Tutorial/TutorialContent.qml 1970-01-01 00:00:00 +0000
1379+++ qml/Tutorial/TutorialContent.qml 2015-02-11 16:31:26 +0000
1380@@ -0,0 +1,130 @@
1381+/*
1382+ * Copyright (C) 2013,2014 Canonical, Ltd.
1383+ *
1384+ * This program is free software; you can redistribute it and/or modify
1385+ * it under the terms of the GNU General Public License as published by
1386+ * the Free Software Foundation; version 3.
1387+ *
1388+ * This program is distributed in the hope that it will be useful,
1389+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1390+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1391+ * GNU General Public License for more details.
1392+ *
1393+ * You should have received a copy of the GNU General Public License
1394+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1395+ */
1396+
1397+import QtQuick 2.3
1398+import Ubuntu.Components 1.1
1399+
1400+Item {
1401+ id: root
1402+
1403+ property Item launcher
1404+ property Item panel
1405+ property Item stages
1406+ property Item overlay
1407+
1408+ readonly property bool launcherEnabled: !running ||
1409+ (!paused && loader.target === leftComponent)
1410+ readonly property bool stagesEnabled: !running
1411+ readonly property bool panelEnabled: !running
1412+ readonly property bool panelContentEnabled: !running
1413+ readonly property bool running: loader.sourceComponent !== null
1414+
1415+ property bool paused: false
1416+
1417+ signal finished()
1418+
1419+ function finish() {
1420+ d.stop();
1421+ finished();
1422+ }
1423+
1424+ ////
1425+
1426+ Component.onCompleted: {
1427+ d.start();
1428+ }
1429+
1430+ QtObject {
1431+ id: d
1432+
1433+ function stop() {
1434+ loader.sourceComponent = null;
1435+ }
1436+
1437+ function start() {
1438+ loader.load(leftComponent);
1439+ }
1440+ }
1441+
1442+ Loader {
1443+ id: loader
1444+ objectName: "tutorialLoader"
1445+
1446+ property Component target: {
1447+ if (next) {
1448+ return next;
1449+ } else if (loader.item && loader.item.shown) {
1450+ return sourceComponent;
1451+ } else {
1452+ return null;
1453+ }
1454+ }
1455+
1456+ property Component next: null
1457+
1458+ function load(comp) {
1459+ if (loader.item) {
1460+ next = comp;
1461+ loader.item.hide();
1462+ } else {
1463+ loader.sourceComponent = comp;
1464+ }
1465+ }
1466+
1467+ Connections {
1468+ target: loader.item
1469+ onFinished: {
1470+ loader.sourceComponent = loader.next;
1471+ if (loader.next != null) {
1472+ loader.next = null;
1473+ } else {
1474+ root.finished();
1475+ }
1476+ }
1477+ }
1478+
1479+ Binding {
1480+ target: loader.item
1481+ property: "paused"
1482+ value: root.paused
1483+ }
1484+ }
1485+
1486+ Component {
1487+ id: leftComponent
1488+ TutorialLeft {
1489+ objectName: "tutorialLeft"
1490+ parent: root.stages
1491+ anchors.fill: parent
1492+ launcher: root.launcher
1493+
1494+ onFinished: loader.load(leftFinishComponent)
1495+ }
1496+ }
1497+
1498+ Component {
1499+ id: leftFinishComponent
1500+ TutorialLeftFinish {
1501+ objectName: "tutorialLeftFinish"
1502+ parent: root.stages
1503+ anchors.fill: parent
1504+ textXOffset: root.launcher.panelWidth
1505+ backgroundFadesOut: true
1506+
1507+ onFinished: root.launcher.hide()
1508+ }
1509+ }
1510+}
1511
1512=== added file 'qml/Tutorial/TutorialLeft.qml'
1513--- qml/Tutorial/TutorialLeft.qml 1970-01-01 00:00:00 +0000
1514+++ qml/Tutorial/TutorialLeft.qml 2015-02-11 16:31:26 +0000
1515@@ -0,0 +1,91 @@
1516+/*
1517+ * Copyright (C) 2014 Canonical, Ltd.
1518+ *
1519+ * This program is free software; you can redistribute it and/or modify
1520+ * it under the terms of the GNU General Public License as published by
1521+ * the Free Software Foundation; version 3.
1522+ *
1523+ * This program is distributed in the hope that it will be useful,
1524+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1525+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1526+ * GNU General Public License for more details.
1527+ *
1528+ * You should have received a copy of the GNU General Public License
1529+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1530+ */
1531+
1532+import QtQuick 2.3
1533+import Ubuntu.Components 1.1
1534+import "." as LocalComponents
1535+
1536+TutorialPage {
1537+ id: root
1538+
1539+ property var launcher
1540+
1541+ title: i18n.tr("Open the launcher")
1542+ text: i18n.tr("Short swipe from the left edge.")
1543+
1544+ textXOffset: root.launcher.x + root.launcher.visibleWidth
1545+
1546+ Connections {
1547+ target: root.launcher
1548+
1549+ onStateChanged: {
1550+ if (root.launcher.state === "visible") {
1551+ finishTimer.start();
1552+ }
1553+ }
1554+
1555+ onDash: {
1556+ finishTimer.stop();
1557+ root.showError();
1558+ root.launcher.hide();
1559+ }
1560+ }
1561+
1562+ SequentialAnimation {
1563+ id: teaseAnimation
1564+ paused: running && root.paused
1565+ running: !slider.active && root.launcher.visibleWidth === 0 && root.shown
1566+ loops: Animation.Infinite
1567+
1568+ UbuntuNumberAnimation {
1569+ target: root.launcher
1570+ property: "x"
1571+ to: units.gu(2)
1572+ duration: UbuntuAnimation.SleepyDuration
1573+ }
1574+ UbuntuNumberAnimation {
1575+ target: root.launcher
1576+ property: "x"
1577+ to: 0
1578+ duration: UbuntuAnimation.SleepyDuration
1579+ }
1580+ }
1581+
1582+ Timer {
1583+ id: finishTimer
1584+ interval: 1
1585+ onTriggered: {
1586+ root.hide();
1587+ root.launcher.x = 0; // make sure to reset launcher before we go
1588+ }
1589+ }
1590+
1591+ foreground {
1592+ children: [
1593+ LocalComponents.Slider {
1594+ id: slider
1595+ anchors {
1596+ left: parent.left
1597+ top: parent.top
1598+ topMargin: root.textBottom + units.gu(3)
1599+ }
1600+ offset: root.launcher.x + root.launcher.visibleWidth + root.launcher.progress
1601+ active: root.launcher.dragging
1602+ shortSwipe: true
1603+ }
1604+ ]
1605+ }
1606+}
1607
1608=== added file 'qml/Tutorial/TutorialLeftFinish.qml'
1609--- qml/Tutorial/TutorialLeftFinish.qml 1970-01-01 00:00:00 +0000
1610+++ qml/Tutorial/TutorialLeftFinish.qml 2015-02-11 16:31:26 +0000
1611@@ -0,0 +1,47 @@
1612+/*
1613+ * Copyright (C) 2014 Canonical, Ltd.
1614+ *
1615+ * This program is free software; you can redistribute it and/or modify
1616+ * it under the terms of the GNU General Public License as published by
1617+ * the Free Software Foundation; version 3.
1618+ *
1619+ * This program is distributed in the hope that it will be useful,
1620+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1621+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1622+ * GNU General Public License for more details.
1623+ *
1624+ * You should have received a copy of the GNU General Public License
1625+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1626+ */
1627+
1628+import QtQuick 2.3
1629+import Ubuntu.Components 1.1
1630+
1631+TutorialPage {
1632+ id: root
1633+
1634+ title: i18n.tr("These are the shortcuts to favorite apps")
1635+ text: i18n.tr("Tap here to finish.")
1636+ fullTextWidth: true
1637+
1638+ foreground {
1639+ children: [
1640+ Image {
1641+ objectName: "tick"
1642+ anchors {
1643+ horizontalCenter: parent.horizontalCenter
1644+ top: parent.top
1645+ topMargin: root.textBottom + units.gu(3)
1646+ }
1647+ source: Qt.resolvedUrl("graphics/tick.png")
1648+ height: units.gu(6.5)
1649+ width: units.gu(6.5)
1650+
1651+ MouseArea {
1652+ anchors.fill: parent
1653+ onClicked: root.hide()
1654+ }
1655+ }
1656+ ]
1657+ }
1658+}
1659
1660=== added file 'qml/Tutorial/TutorialPage.qml'
1661--- qml/Tutorial/TutorialPage.qml 1970-01-01 00:00:00 +0000
1662+++ qml/Tutorial/TutorialPage.qml 2015-02-11 16:31:26 +0000
1663@@ -0,0 +1,240 @@
1664+/*
1665+ * Copyright (C) 2013,2014 Canonical, Ltd.
1666+ *
1667+ * This program is free software; you can redistribute it and/or modify
1668+ * it under the terms of the GNU General Public License as published by
1669+ * the Free Software Foundation; version 3.
1670+ *
1671+ * This program is distributed in the hope that it will be useful,
1672+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1673+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1674+ * GNU General Public License for more details.
1675+ *
1676+ * You should have received a copy of the GNU General Public License
1677+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1678+ */
1679+
1680+import QtQuick 2.3
1681+import Ubuntu.Components 1.1
1682+import "../Components"
1683+
1684+Showable {
1685+ id: root
1686+
1687+ // This is the header displayed, like "Right edge"
1688+ property alias title: titleLabel.text
1689+
1690+ // This is the block of text displayed below the header
1691+ property alias text: textLabel.text
1692+
1693+ // Whether animations are paused
1694+ property bool paused
1695+
1696+ // Whether to give the text the full width that the title has
1697+ property bool fullTextWidth
1698+
1699+ // Whether whole page (background + foreground) or just the foreground fades in
1700+ property bool backgroundFadesIn: false
1701+
1702+ // Whether whole page (background + foreground) or just the foreground fades out
1703+ property bool backgroundFadesOut: false
1704+
1705+ // The foreground Item, add children to it that you want to fade in
1706+ property alias foreground: foregroundExtra
1707+
1708+ // The text label bottom, so you can position elements relative to it
1709+ readonly property real textBottom: Math.max(textLabel.y + textLabel.height, errorTextLabel.y + errorTextLabel.height)
1710+
1711+ // The MouseArea that eats events (so you can adjust size as you will)
1712+ property alias mouseArea: mouseArea
1713+
1714+ // X/Y offsets for text
1715+ property real textXOffset: 0
1716+ property real textYOffset: 0
1717+
1718+ // Foreground opacity
1719+ property real foregroundOpacity: 1
1720+
1721+ signal finished()
1722+
1723+ function showError() {
1724+ errorTimer.start();
1725+ }
1726+
1727+ ////
1728+
1729+ shown: false
1730+ Component.onCompleted: show()
1731+
1732+ property real _foregroundHideOpacity
1733+
1734+ showAnimation: StandardAnimation {
1735+ property: root.backgroundFadesIn ? "opacity" : "_foregroundHideOpacity"
1736+ from: 0
1737+ to: 1
1738+ duration: root.backgroundFadesIn ? UbuntuAnimation.SleepyDuration : UbuntuAnimation.BriskDuration
1739+ }
1740+
1741+ hideAnimation: StandardAnimation {
1742+ property: root.backgroundFadesOut ? "opacity" : "_foregroundHideOpacity"
1743+ to: 0
1744+ duration: UbuntuAnimation.BriskDuration
1745+ onRunningChanged: {
1746+ if (!running) {
1747+ root.finished();
1748+ }
1749+ }
1750+ }
1751+
1752+ QtObject {
1753+ id: d
1754+
1755+ readonly property real sideMargin: units.gu(5.5)
1756+ readonly property real verticalOffset: -units.gu(9)
1757+ readonly property real textXOffset: Math.max(0, root.textXOffset - sideMargin + units.gu(2))
1758+
1759+ property real fadeInOffset: {
1760+ if (showAnimation.running) {
1761+ var opacity = root[root.showAnimation.property]
1762+ return (1 - opacity) * units.gu(3);
1763+ } else {
1764+ return 0;
1765+ }
1766+ }
1767+ }
1768+
1769+ Timer {
1770+ id: errorTimer
1771+ interval: 3500
1772+ }
1773+
1774+ MouseArea { // eat any errant presses
1775+ id: mouseArea
1776+ anchors.fill: parent
1777+ }
1778+
1779+ Rectangle {
1780+ anchors.fill: parent
1781+ color: "black"
1782+ opacity: 0.82
1783+ }
1784+
1785+ Item {
1786+ id: foreground
1787+ anchors.fill: parent
1788+ opacity: root.foregroundOpacity < 1 ? root.foregroundOpacity : root._foregroundHideOpacity
1789+
1790+ Label {
1791+ id: titleLabel
1792+ anchors {
1793+ top: parent.verticalCenter
1794+ topMargin: d.verticalOffset + root.textYOffset
1795+ left: parent.left
1796+ leftMargin: d.sideMargin + d.textXOffset
1797+ }
1798+ width: parent.width - d.sideMargin * 2
1799+ horizontalAlignment: Text.AlignLeft
1800+ wrapMode: Text.Wrap
1801+ font.weight: Font.Light
1802+ font.pixelSize: units.gu(3.5)
1803+ }
1804+
1805+ Label {
1806+ id: textLabel
1807+ anchors {
1808+ top: titleLabel.bottom
1809+ topMargin: units.gu(2)
1810+ left: parent.left
1811+ leftMargin: d.sideMargin + d.textXOffset
1812+ }
1813+ width: (parent.width - d.sideMargin * 2) * (fullTextWidth ? 1 : 0.66)
1814+ horizontalAlignment: Text.AlignLeft
1815+ wrapMode: Text.Wrap
1816+ font.weight: Font.Light
1817+ font.pixelSize: units.gu(2.5)
1818+ }
1819+
1820+ // We use two separate labels like this rather than just changing
1821+ // the text of the above labels because we want to know where to place
1822+ // sliders (via root.textBottom) without having that place change
1823+ // as the text changes length.
1824+ Label {
1825+ id: errorTitleLabel
1826+ objectName: "errorTitleLabel"
1827+ anchors {
1828+ top: titleLabel.top
1829+ left: titleLabel.left
1830+ }
1831+ width: titleLabel.width
1832+ horizontalAlignment: titleLabel.horizontalAlignment
1833+ wrapMode: titleLabel.wrapMode
1834+ font.weight: titleLabel.font.weight
1835+ font.pixelSize: titleLabel.font.pixelSize
1836+ opacity: 0
1837+ text: i18n.tr("You almost got it!")
1838+ }
1839+
1840+ Label {
1841+ id: errorTextLabel
1842+ objectName: "errorTextLabel"
1843+ anchors {
1844+ top: errorTitleLabel.bottom
1845+ topMargin: textLabel.anchors.topMargin
1846+ left: textLabel.left
1847+ }
1848+ width: textLabel.width
1849+ horizontalAlignment: textLabel.horizontalAlignment
1850+ wrapMode: textLabel.wrapMode
1851+ font.weight: textLabel.font.weight
1852+ font.pixelSize: textLabel.font.pixelSize
1853+ opacity: 0
1854+ text: i18n.tr("Try again.")
1855+ }
1856+
1857+ // A place for subclasses to add extra widgets
1858+ Item {
1859+ id: foregroundExtra
1860+ anchors.fill: parent
1861+ }
1862+ }
1863+
1864+ states: State {
1865+ name: "errorState"
1866+ when: errorTimer.running
1867+ PropertyChanges { target: titleLabel; opacity: 0 }
1868+ PropertyChanges { target: textLabel; opacity: 0 }
1869+ PropertyChanges { target: errorTitleLabel; opacity: 1 }
1870+ PropertyChanges { target: errorTextLabel; opacity: 1 }
1871+ }
1872+
1873+ transitions: Transition {
1874+ to: "errorState"
1875+ reversible: true
1876+ SequentialAnimation {
1877+ ParallelAnimation {
1878+ StandardAnimation {
1879+ target: titleLabel
1880+ property: "opacity"
1881+ duration: UbuntuAnimation.BriskDuration
1882+ }
1883+ StandardAnimation {
1884+ target: textLabel
1885+ property: "opacity"
1886+ duration: UbuntuAnimation.BriskDuration
1887+ }
1888+ }
1889+ ParallelAnimation {
1890+ StandardAnimation {
1891+ target: errorTitleLabel
1892+ property: "opacity"
1893+ duration: UbuntuAnimation.BriskDuration
1894+ }
1895+ StandardAnimation {
1896+ target: errorTextLabel
1897+ property: "opacity"
1898+ duration: UbuntuAnimation.BriskDuration
1899+ }
1900+ }
1901+ }
1902+ }
1903+}
1904
1905=== added directory 'qml/Tutorial/graphics'
1906=== added file 'qml/Tutorial/graphics/chevron.png'
1907Binary files qml/Tutorial/graphics/chevron.png 1970-01-01 00:00:00 +0000 and qml/Tutorial/graphics/chevron.png 2015-02-11 16:31:26 +0000 differ
1908=== added file 'qml/Tutorial/graphics/tick.png'
1909Binary files qml/Tutorial/graphics/tick.png 1970-01-01 00:00:00 +0000 and qml/Tutorial/graphics/tick.png 2015-02-11 16:31:26 +0000 differ
1910=== renamed file 'tests/autopilot/unity8/shell/emulators/edges_demo.py' => 'tests/autopilot/unity8/shell/emulators/tutorial.py'
1911--- tests/autopilot/unity8/shell/emulators/edges_demo.py 2014-12-16 16:32:40 +0000
1912+++ tests/autopilot/unity8/shell/emulators/tutorial.py 2015-02-11 16:31:26 +0000
1913@@ -29,112 +29,26 @@
1914 logger = logging.getLogger(__name__)
1915
1916
1917-class RightEdgeDemoOverlay(
1918- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1919-
1920- @classmethod
1921- def validate_dbus_object(cls, path, state):
1922- name = introspection.get_classname_from_path(path)
1923- if name == b'EdgeDemoOverlay':
1924- if state['edge'][1] == 'right':
1925- return True
1926- return False
1927-
1928- @autopilot.logging.log_action(logger.info)
1929- def swipe(self):
1930- """Swipe to the left to complete this demo step."""
1931- x, y, width, height = self.globalRect
1932- start_x = x + width
1933- stop_x = x
1934- start_y = stop_y = y + height // 2
1935- self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
1936- return self.get_root_instance().wait_select_single(
1937- edge='top', active=True)
1938-
1939-
1940-class TopEdgeDemoOverlay(
1941- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1942-
1943- @classmethod
1944- def validate_dbus_object(cls, path, state):
1945- name = introspection.get_classname_from_path(path)
1946- if name == b'EdgeDemoOverlay':
1947- if state['edge'][1] == 'top':
1948- return True
1949- return False
1950-
1951- @autopilot.logging.log_action(logger.info)
1952- def swipe(self):
1953- """Swipe to the bottom to complete this demo step."""
1954- x, y, width, height = self.globalRect
1955- start_x = stop_x = x + width // 2
1956- start_y = y
1957- stop_y = y + height
1958- self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
1959- return self.get_root_instance().wait_select_single(
1960- edge='bottom', active=True)
1961-
1962-
1963-class BottomEdgeDemoOverlay(
1964- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1965-
1966- @classmethod
1967- def validate_dbus_object(cls, path, state):
1968- name = introspection.get_classname_from_path(path)
1969- if name == b'EdgeDemoOverlay':
1970- if state['edge'][1] == 'bottom':
1971- return True
1972- return False
1973-
1974- @autopilot.logging.log_action(logger.info)
1975- def swipe(self):
1976- """Swipe to the top to complete this demo step."""
1977- x, y, width, height = self.globalRect
1978- start_x = stop_x = x + width // 2
1979- start_y = y + height
1980- stop_y = y
1981- self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
1982- return self.get_root_instance().wait_select_single(
1983- edge='left', active=True)
1984-
1985-
1986-class LeftEdgeDemoOverlay(
1987- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1988-
1989- @classmethod
1990- def validate_dbus_object(cls, path, state):
1991- name = introspection.get_classname_from_path(path)
1992- if name == b'EdgeDemoOverlay':
1993- if state['edge'][1] == 'left':
1994- return True
1995- return False
1996-
1997- @autopilot.logging.log_action(logger.info)
1998- def swipe(self):
1999- """Swipe to the right to complete this demo step."""
2000+class TutorialPage(
2001+ ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2002+
2003+ @classmethod
2004+ def validate_dbus_object(cls, path, state):
2005+ name = introspection.get_classname_from_path(path)
2006+ return name == b'TutorialPage' or name == b'TutorialLeft'
2007+
2008+ @autopilot.logging.log_action(logger.info)
2009+ def short_swipe_right(self):
2010 x, y, width, height = self.globalRect
2011 start_x = x
2012- stop_x = x + width
2013+ stop_x = x + width // 3
2014 start_y = stop_y = y + height // 2
2015 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
2016- return self.get_root_instance().wait_select_single(
2017- edge='none', active=True)
2018-
2019-
2020-class FinalEdgeDemoOverlay(
2021- ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
2022-
2023- @classmethod
2024- def validate_dbus_object(cls, path, state):
2025- name = introspection.get_classname_from_path(path)
2026- if name == b'EdgeDemoOverlay':
2027- if state['edge'][1] == 'none':
2028- return True
2029- return False
2030+ self.shown.wait_for(False)
2031
2032 @autopilot.logging.log_action(logger.info)
2033- def tap_to_start(self):
2034- """Tap to finish the demo and start using the Ubuntu Touch."""
2035- time.sleep(1)
2036- self.pointing_device.click_object(self)
2037+ def tap(self):
2038+ """Tap the tick button to complete this step."""
2039+ button = self.select_single(objectName="tick")
2040+ self.pointing_device.click_object(button)
2041 self.shown.wait_for(False)
2042
2043=== modified file 'tests/autopilot/unity8/shell/fixture_setup.py'
2044--- tests/autopilot/unity8/shell/fixture_setup.py 2014-12-16 16:32:40 +0000
2045+++ tests/autopilot/unity8/shell/fixture_setup.py 2015-02-11 16:31:26 +0000
2046@@ -54,7 +54,7 @@
2047 return ld_library_path
2048
2049
2050-class EdgesDemo(fixtures.Fixture):
2051+class Tutorial(fixtures.Fixture):
2052
2053 def __init__(self, enable):
2054 super().__init__()
2055@@ -62,12 +62,12 @@
2056
2057 def setUp(self):
2058 super().setUp()
2059- original_state = self._is_edges_demo_enabled()
2060+ original_state = self._is_tutorial_enabled()
2061 if self.enable != original_state:
2062- self.addCleanup(self._set_edges_demo, original_state)
2063- self._set_edges_demo(self.enable)
2064+ self.addCleanup(self._set_tutorial, original_state)
2065+ self._set_tutorial(self.enable)
2066
2067- def _is_edges_demo_enabled(self):
2068+ def _is_tutorial_enabled(self):
2069 command = [
2070 'dbus-send', '--system', '--print-reply',
2071 '--dest=org.freedesktop.Accounts',
2072@@ -79,7 +79,7 @@
2073 output = subprocess.check_output(command, universal_newlines=True)
2074 return True if output.count('true') else False
2075
2076- def _set_edges_demo(self, value):
2077+ def _set_tutorial(self, value):
2078 value_string = 'true' if value else 'false'
2079 command = [
2080 'dbus-send', '--system', '--print-reply',
2081
2082=== renamed file 'tests/autopilot/unity8/shell/tests/test_edges_demo.py' => 'tests/autopilot/unity8/shell/tests/test_tutorial.py'
2083--- tests/autopilot/unity8/shell/tests/test_edges_demo.py 2015-01-21 13:50:14 +0000
2084+++ tests/autopilot/unity8/shell/tests/test_tutorial.py 2015-02-11 16:31:26 +0000
2085@@ -24,28 +24,27 @@
2086 fixture_setup,
2087 tests
2088 )
2089-# unused import to load the edge emulators custom proxy objects.
2090-from unity8.shell.emulators import edges_demo # NOQA
2091-
2092-
2093-class EdgesDemoTestCase(tests.UnityTestCase):
2094+# unused import to load the tutorial emulators custom proxy objects.
2095+from unity8.shell.emulators import tutorial # NOQA
2096+
2097+
2098+class TutorialTestCase(tests.UnityTestCase):
2099
2100 def setUp(self):
2101- super(EdgesDemoTestCase, self).setUp()
2102+ super(TutorialTestCase, self).setUp()
2103 self._qml_mock_enabled = False
2104 self._data_dirs_mock_enabled = False
2105
2106- self.useFixture(fixture_setup.EdgesDemo(True))
2107+ self.useFixture(fixture_setup.Tutorial(True))
2108 self.unity = self.launch_unity()
2109
2110- def test_complete_edge_demo(self):
2111- edge_demo = self.unity.select_single('EdgeDemo')
2112- self.assertThat(edge_demo.running, Eventually(Equals(True)))
2113- right_edge_overlay = self.unity.wait_select_single(
2114- edge='right', active=True)
2115- top_edge_overlay = right_edge_overlay.swipe()
2116- bottom_edge_overlay = top_edge_overlay.swipe()
2117- left_edge_overlay = bottom_edge_overlay.swipe()
2118- final_overlay = left_edge_overlay.swipe()
2119- final_overlay.tap_to_start()
2120- self.assertThat(edge_demo.running, Eventually(Equals(False)))
2121+ def test_complete_tutorial(self):
2122+ greeter = self.main_window.get_greeter()
2123+ tutorial = self.unity.select_single('Tutorial')
2124+ self.assertThat(tutorial.running, Eventually(Equals(True)))
2125+ greeter.swipe()
2126+ page = self.unity.wait_select_single(objectName='tutorialLeft')
2127+ page.short_swipe_right()
2128+ page = self.unity.wait_select_single(objectName='tutorialLeftFinish')
2129+ page.tap()
2130+ self.assertThat(tutorial.running, Eventually(Equals(False)))
2131
2132=== modified file 'tests/mocks/Unity/Application/MirSurfaceItem.qml'
2133--- tests/mocks/Unity/Application/MirSurfaceItem.qml 2014-10-01 13:20:32 +0000
2134+++ tests/mocks/Unity/Application/MirSurfaceItem.qml 2015-02-11 16:31:26 +0000
2135@@ -44,9 +44,24 @@
2136 }
2137
2138 Text {
2139+ text: surfaceText.text
2140+ color: "black"
2141+ font: surfaceText.font
2142+ fontSizeMode: Text.Fit
2143+ minimumPixelSize: 10
2144+ verticalAlignment: Text.AlignVCenter
2145+ x: surfaceText.x
2146+ y: surfaceText.y
2147+ width: surfaceText.width
2148+ height: surfaceText.height
2149+
2150+ transform: Translate { x: -2; y: -2 }
2151+ }
2152+ Text {
2153+ id: surfaceText
2154 anchors.fill: parent
2155 text: "SURFACE"
2156- color: "yellow"
2157+ color: root.parent && root.parent.activeFocus ? "yellow" : "blue"
2158 font.bold: true
2159 fontSizeMode: Text.Fit
2160 minimumPixelSize: 10; font.pixelSize: 200
2161
2162=== modified file 'tests/qmltests/CMakeLists.txt'
2163--- tests/qmltests/CMakeLists.txt 2015-01-22 11:56:24 +0000
2164+++ tests/qmltests/CMakeLists.txt 2015-02-11 16:31:26 +0000
2165@@ -25,7 +25,6 @@
2166 add_qml_test(Components Carousel)
2167 add_qml_test(Components Dialogs)
2168 add_qml_test(Components DraggingArea)
2169-add_qml_test(Components EdgeDemoOverlay)
2170 add_qml_test(Components LazyImage)
2171 add_qml_test(Components Lockscreen)
2172 add_qml_test(Components Rating)
2173@@ -88,6 +87,8 @@
2174 add_qml_test(Stages SpreadDelegate ENVIRONMENT)
2175 add_qml_test(Stages SurfaceContainer ENVIRONMENT)
2176 add_qml_test(Stages SessionContainer ENVIRONMENT)
2177+add_qml_test(Stages TabletStage)
2178 add_qml_test(Stages WindowMoveResizeArea)
2179 add_qml_test(Stages Splash)
2180+add_qml_test(Tutorial Tutorial)
2181 add_qml_test(Wizard Wizard)
2182
2183=== removed file 'tests/qmltests/Components/tst_EdgeDemoOverlay.qml'
2184--- tests/qmltests/Components/tst_EdgeDemoOverlay.qml 2013-12-17 16:04:47 +0000
2185+++ tests/qmltests/Components/tst_EdgeDemoOverlay.qml 1970-01-01 00:00:00 +0000
2186@@ -1,123 +0,0 @@
2187-/*
2188- * Copyright 2013 Canonical Ltd.
2189- *
2190- * This program is free software; you can redistribute it and/or modify
2191- * it under the terms of the GNU General Public License as published by
2192- * the Free Software Foundation; version 3.
2193- *
2194- * This program is distributed in the hope that it will be useful,
2195- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2196- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2197- * GNU General Public License for more details.
2198- *
2199- * You should have received a copy of the GNU General Public License
2200- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2201- */
2202-
2203-import QtQuick 2.0
2204-import QtTest 1.0
2205-import Unity.Test 0.1 as UT
2206-import "../../../qml/Components"
2207-
2208-Item {
2209- id: root
2210- width: boxWidth * 3
2211- height: boxHeight * 2
2212-
2213- property int boxWidth: 250
2214- property int boxHeight: 250
2215-
2216- EdgeDemoOverlay {
2217- id: top
2218- edge: "top"
2219- title: "Top"
2220- text: "Displayed on top left"
2221- anchors.left: parent.left
2222- anchors.top: parent.top
2223- width: boxWidth
2224- height: boxHeight
2225- }
2226-
2227- EdgeDemoOverlay {
2228- id: right
2229- edge: "right"
2230- title: "Right"
2231- text: "Displayed on top right"
2232- anchors.right: parent.right
2233- anchors.top: parent.top
2234- width: boxWidth
2235- height: boxHeight
2236- }
2237-
2238- EdgeDemoOverlay {
2239- id: left
2240- edge: "left"
2241- title: "Left"
2242- text: "Displayed on bottom right"
2243- anchors.right: parent.right
2244- anchors.bottom: parent.bottom
2245- width: boxWidth
2246- height: boxHeight
2247- available: false
2248- }
2249-
2250- EdgeDemoOverlay {
2251- id: bottom
2252- edge: "bottom"
2253- title: "Bottom"
2254- text: "Displayed on bottom left"
2255- anchors.left: parent.left
2256- anchors.bottom: parent.bottom
2257- width: boxWidth
2258- height: boxHeight
2259- }
2260-
2261- EdgeDemoOverlay {
2262- id: none
2263- edge: "none"
2264- title: "None"
2265- text: "Displayed on top middle"
2266- anchors.horizontalCenter: parent.horizontalCenter
2267- anchors.top: parent.top
2268- width: boxWidth
2269- height: boxHeight
2270- }
2271-
2272- SignalSpy {
2273- id: signalSpy
2274- }
2275-
2276- UT.UnityTestCase {
2277- name: "EdgeDemoOverlay"
2278- when: windowShown
2279-
2280- function test_animations() {
2281- compare(right.running, true)
2282-
2283- compare(left.running, false)
2284- left.available = true
2285- compare(left.running, true)
2286- }
2287-
2288- function test_skip() {
2289- signalSpy.target = bottom
2290- signalSpy.signalName = "skip"
2291- signalSpy.clear()
2292- var bottomSkip = findChild(bottom, "skipLabel")
2293- mousePress(bottomSkip, 1, 1)
2294- mouseRelease(bottomSkip, 1, 1)
2295- signalSpy.wait()
2296- compare(bottom.available, false)
2297-
2298- // Test that the 'none' edge skips anywhere
2299- signalSpy.target = none
2300- signalSpy.clear()
2301- var backgroundShade = findChild(none, "backgroundShadeMouseArea")
2302- tryCompare(backgroundShade, "enabled", true)
2303- mousePress(backgroundShade, 1, 1)
2304- mouseRelease(backgroundShade, 1, 1)
2305- signalSpy.wait()
2306- compare(none.available, false)
2307- }
2308- }
2309-}
2310
2311=== added file 'tests/qmltests/Stages/ApplicationCheckBox.qml'
2312--- tests/qmltests/Stages/ApplicationCheckBox.qml 1970-01-01 00:00:00 +0000
2313+++ tests/qmltests/Stages/ApplicationCheckBox.qml 2015-02-11 16:31:26 +0000
2314@@ -0,0 +1,45 @@
2315+/*
2316+ * Copyright (C) 2015 Canonical, Ltd.
2317+ *
2318+ * This program is free software; you can redistribute it and/or modify
2319+ * it under the terms of the GNU General Public License as published by
2320+ * the Free Software Foundation; version 3.
2321+ *
2322+ * This program is distributed in the hope that it will be useful,
2323+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2324+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2325+ * GNU General Public License for more details.
2326+ *
2327+ * You should have received a copy of the GNU General Public License
2328+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2329+ */
2330+
2331+import QtQuick 2.0
2332+import QtQuick.Layouts 1.1
2333+import Ubuntu.Components 1.1
2334+import Unity.Application 0.1
2335+
2336+RowLayout {
2337+ id: root
2338+ property string appId
2339+ property alias checked: checkbox.checked
2340+
2341+ Layout.fillWidth: true
2342+ CheckBox {
2343+ id: checkbox
2344+ checked: false
2345+ activeFocusOnPress: false
2346+ onCheckedChanged: {
2347+ if (checked) {
2348+ ApplicationManager.startApplication(root.appId);
2349+ } else {
2350+ ApplicationManager.stopApplication(root.appId);
2351+ }
2352+ }
2353+ }
2354+ Label {
2355+ text: root.appId
2356+ color: "white"
2357+ anchors.verticalCenter: parent.verticalCenter
2358+ }
2359+}
2360
2361=== modified file 'tests/qmltests/Stages/RecursingChildSessionControl.qml'
2362--- tests/qmltests/Stages/RecursingChildSessionControl.qml 2014-09-10 16:06:05 +0000
2363+++ tests/qmltests/Stages/RecursingChildSessionControl.qml 2015-02-11 16:31:26 +0000
2364@@ -89,6 +89,7 @@
2365 CheckBox {
2366 id: _surfaceCheckbox;
2367 checked: false;
2368+ activeFocusOnPress: false
2369 enabled: root.session
2370 onCheckedChanged: {
2371 if (checked) {
2372@@ -117,6 +118,7 @@
2373
2374 Button {
2375 enabled: root.session
2376+ activeFocusOnPress: false
2377 text: removable ? "Remove" : "Release"
2378 onClicked: {
2379 if (removable) {
2380@@ -131,6 +133,7 @@
2381
2382 Button {
2383 enabled: root.session !== null
2384+ activeFocusOnPress: false
2385 text: "Add Child"
2386 onClicked: {
2387 var screenshot = Math.round(Math.random()*100 % (screenshotIds.length-1));
2388
2389=== modified file 'tests/qmltests/Stages/tst_ApplicationWindow.qml'
2390--- tests/qmltests/Stages/tst_ApplicationWindow.qml 2014-10-01 18:16:04 +0000
2391+++ tests/qmltests/Stages/tst_ApplicationWindow.qml 2015-02-11 16:31:26 +0000
2392@@ -52,10 +52,12 @@
2393 application: fakeApplication
2394 orientation: Qt.PortraitOrientation
2395 interactive: true
2396+ focus: true
2397 }
2398 }
2399 Loader {
2400 id: applicationWindowLoader
2401+ focus: true
2402 anchors {
2403 top: parent.top
2404 bottom: parent.bottom
2405@@ -83,6 +85,7 @@
2406
2407 CheckBox {
2408 id: sessionCheckbox; checked: false
2409+ activeFocusOnPress: false
2410 onCheckedChanged: {
2411 if (applicationWindowLoader.status !== Loader.Ready)
2412 return;
2413@@ -121,6 +124,7 @@
2414
2415 ListItem.ItemSelector {
2416 id: appStateSelector
2417+ activeFocusOnPress: false
2418 anchors { left: parent.left; right: parent.right }
2419 text: "Application state"
2420 model: ["Starting",
2421@@ -146,6 +150,7 @@
2422
2423 Button {
2424 anchors { left: parent.left; right: parent.right }
2425+ activeFocusOnPress: false
2426 text: "Rotate device \u27F3"
2427 onClicked: {
2428 var orientation = applicationWindowLoader.item.orientation
2429@@ -398,7 +403,7 @@
2430 verify(stateGroup.state === "void");
2431 }
2432
2433- function test_forceActiveFocusFollowsInterative() {
2434+ function test_surfaceActiveFocusFollowsAppWindowInterative() {
2435 fakeApplication.createSession();
2436 applicationWindowLoader.item.interactive = false;
2437 applicationWindowLoader.item.interactive = true;
2438
2439=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
2440--- tests/qmltests/Stages/tst_PhoneStage.qml 2015-01-12 11:21:17 +0000
2441+++ tests/qmltests/Stages/tst_PhoneStage.qml 2015-02-11 16:31:26 +0000
2442@@ -29,6 +29,7 @@
2443 PhoneStage {
2444 id: phoneStage
2445 anchors { fill: parent; rightMargin: units.gu(30) }
2446+ focus: true
2447 dragAreaWidth: units.gu(2)
2448 maximizedAppTopMargin: units.gu(3) + units.dp(2)
2449 interactive: true
2450@@ -51,6 +52,7 @@
2451 Button {
2452 anchors { left: parent.left; right: parent.right }
2453 text: "Add App"
2454+ activeFocusOnPress: false
2455 onClicked: {
2456 testCase.addApps();
2457 }
2458@@ -58,6 +60,7 @@
2459 Button {
2460 anchors { left: parent.left; right: parent.right }
2461 text: "Remove Selected"
2462+ activeFocusOnPress: false
2463 onClicked: {
2464 ApplicationManager.stopApplication(ApplicationManager.get(appList.selectedAppIndex).appId);
2465 }
2466@@ -65,6 +68,7 @@
2467 Button {
2468 anchors { left: parent.left; right: parent.right }
2469 text: "Stop Selected"
2470+ activeFocusOnPress: false
2471 onClicked: {
2472 ApplicationManager.get(appList.selectedAppIndex).setState(ApplicationInfoInterface.Stopped);
2473 }
2474
2475=== modified file 'tests/qmltests/Stages/tst_SessionContainer.qml'
2476--- tests/qmltests/Stages/tst_SessionContainer.qml 2014-10-24 14:31:29 +0000
2477+++ tests/qmltests/Stages/tst_SessionContainer.qml 2015-02-11 16:31:26 +0000
2478@@ -42,11 +42,14 @@
2479 id: sessionContainer
2480 anchors.fill: parent
2481 orientation: Qt.PortraitOrientation
2482+ focus: true
2483+ interactive: true
2484 }
2485 }
2486
2487 Loader {
2488 id: sessionContainerLoader
2489+ focus: true
2490 anchors {
2491 top: parent.top
2492 bottom: parent.bottom
2493@@ -75,6 +78,7 @@
2494 CheckBox {
2495 id: sessionCheckbox;
2496 checked: false;
2497+ activeFocusOnPress: false
2498 onCheckedChanged: {
2499 if (sessionContainerLoader.status !== Loader.Ready)
2500 return;
2501@@ -190,8 +194,6 @@
2502 var sessionContainer = sessionContainerLoader.item;
2503 compare(sessionContainer.childSessions.count(), 0);
2504
2505- sessionContainer.interactive = true;
2506-
2507 var i;
2508 var sessions = [];
2509 // 3 sessions should cover all edge cases
2510@@ -209,14 +211,19 @@
2511
2512 var a_session;
2513 while(a_session = sessions.pop()) {
2514- a_session.surface.forceActiveFocus();
2515 compare(a_session.surface.activeFocus, true);
2516
2517- var parentSession = a_session.parentSession;
2518- sessionContainer.session.removeChildSession(a_session);
2519- compare(a_session.surface.activeFocus, false);
2520+ ApplicationTest.removeSurface(a_session.surface);
2521+ ApplicationTest.removeSession(a_session);
2522
2523- compare(parentSession.surface.activeFocus, true);
2524+ if (sessions.length > 0) {
2525+ // active focus should have gone to the yongest remaining sibling
2526+ var previousSiblingSurface = sessions[sessions.length - 1].surface
2527+ tryCompare(previousSiblingSurface, "activeFocus", true);
2528+ } else {
2529+ // active focus should have gone to the parent surface
2530+ tryCompare(sessionContainer.session.surface, "activeFocus", true);
2531+ }
2532 }
2533 }
2534
2535
2536=== added file 'tests/qmltests/Stages/tst_TabletStage.qml'
2537--- tests/qmltests/Stages/tst_TabletStage.qml 1970-01-01 00:00:00 +0000
2538+++ tests/qmltests/Stages/tst_TabletStage.qml 2015-02-11 16:31:26 +0000
2539@@ -0,0 +1,154 @@
2540+/*
2541+ * Copyright (C) 2015 Canonical, Ltd.
2542+ *
2543+ * This program is free software; you can redistribute it and/or modify
2544+ * it under the terms of the GNU General Public License as published by
2545+ * the Free Software Foundation; version 3.
2546+ *
2547+ * This program is distributed in the hope that it will be useful,
2548+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2549+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2550+ * GNU General Public License for more details.
2551+ *
2552+ * You should have received a copy of the GNU General Public License
2553+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2554+ */
2555+
2556+import QtQuick 2.0
2557+import QtTest 1.0
2558+import Ubuntu.Components 1.1
2559+import Ubuntu.Components.ListItems 1.0 as ListItem
2560+import Unity.Application 0.1
2561+import Unity.Test 0.1
2562+
2563+import "../../../qml/Stages"
2564+
2565+Rectangle {
2566+ id: root
2567+ color: "grey"
2568+ width: tabletStageLoader.width + controls.width
2569+ height: tabletStageLoader.height
2570+
2571+ Loader {
2572+ id: tabletStageLoader
2573+
2574+ x: ((root.width - controls.width) - width) / 2
2575+ y: (root.height - height) / 2
2576+ width: units.gu(160*0.7)
2577+ height: units.gu(100*0.7)
2578+
2579+ focus: true
2580+
2581+ property bool itemDestroyed: false
2582+ sourceComponent: Component {
2583+ TabletStage {
2584+ anchors.fill: parent
2585+ Component.onDestruction: {
2586+ tabletStageLoader.itemDestroyed = true;
2587+ }
2588+ dragAreaWidth: units.gu(2)
2589+ maximizedAppTopMargin: units.gu(3) + units.dp(2)
2590+ interactive: true
2591+ focus: true
2592+ }
2593+ }
2594+ }
2595+
2596+ Rectangle {
2597+ id: controls
2598+ color: "darkgrey"
2599+ width: units.gu(30)
2600+ anchors {
2601+ top: parent.top
2602+ bottom: parent.bottom
2603+ right: parent.right
2604+ }
2605+
2606+ Column {
2607+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
2608+ spacing: units.gu(1)
2609+ ApplicationCheckBox {
2610+ id: webbrowserCheckBox
2611+ appId: "webbrowser-app"
2612+ }
2613+ ApplicationCheckBox {
2614+ id: dialerCheckBox
2615+ appId: "dialer-app"
2616+ }
2617+ }
2618+ }
2619+
2620+ UnityTestCase {
2621+ id: testCase
2622+ name: "TabletStage"
2623+ when: windowShown
2624+
2625+ property Item tabletStage: tabletStageLoader.status === Loader.Ready ? tabletStageLoader.item : null
2626+
2627+ function init() {
2628+ tabletStageLoader.active = true;
2629+ tryCompare(tabletStageLoader, "status", Loader.Ready);
2630+ }
2631+
2632+ function cleanup() {
2633+ tabletStageLoader.itemDestroyed = false;
2634+ tabletStageLoader.active = false;
2635+
2636+ tryCompare(tabletStageLoader, "status", Loader.Null);
2637+ tryCompare(tabletStageLoader, "item", null);
2638+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
2639+ // actually took place. Likely because Loader waits until the next event loop
2640+ // iteration to do its work. So to ensure the reload, we will wait until the
2641+ // Shell instance gets destroyed.
2642+ tryCompare(tabletStageLoader, "itemDestroyed", true);
2643+
2644+ // kill all (fake) running apps
2645+ webbrowserCheckBox.checked = false;
2646+ dialerCheckBox.checked = false;
2647+ }
2648+
2649+ function waitUntilAppSurfaceShowsUp(appId) {
2650+ var appWindow = findChild(tabletStage, "appWindow_" + appId);
2651+ verify(appWindow);
2652+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
2653+ verify(appWindowStates);
2654+ tryCompare(appWindowStates, "state", "surface");
2655+ }
2656+
2657+ function test_tappingSwitchesFocusBetweenStages() {
2658+ webbrowserCheckBox.checked = true;
2659+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
2660+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
2661+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
2662+ tryCompare(webbrowserApp.session.surface, "activeFocus", true);
2663+
2664+ dialerCheckBox.checked = true;
2665+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
2666+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
2667+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
2668+ tryCompare(dialerApp.session.surface, "activeFocus", true);
2669+ tryCompare(webbrowserApp.session.surface, "activeFocus", false);
2670+
2671+ // Tap on the main stage application and check if the focus
2672+ // has been passed to it.
2673+
2674+ var webbrowserWindow = findChild(tabletStage, "appWindow_" + webbrowserApp.appId);
2675+ verify(webbrowserWindow);
2676+ tap(webbrowserWindow);
2677+
2678+ tryCompare(dialerApp.session.surface, "activeFocus", false);
2679+ tryCompare(webbrowserApp.session.surface, "activeFocus", true);
2680+
2681+ // Now tap on the side stage application and check if the focus
2682+ // has been passed back to it.
2683+
2684+ var dialerWindow = findChild(tabletStage, "appWindow_" + dialerApp.appId);
2685+ verify(dialerWindow);
2686+ tap(dialerWindow);
2687+
2688+ tryCompare(dialerApp.session.surface, "activeFocus", true);
2689+ tryCompare(webbrowserApp.session.surface, "activeFocus", false);
2690+ }
2691+ }
2692+
2693+}
2694
2695=== added directory 'tests/qmltests/Tutorial'
2696=== added file 'tests/qmltests/Tutorial/tst_Tutorial.qml'
2697--- tests/qmltests/Tutorial/tst_Tutorial.qml 1970-01-01 00:00:00 +0000
2698+++ tests/qmltests/Tutorial/tst_Tutorial.qml 2015-02-11 16:31:26 +0000
2699@@ -0,0 +1,286 @@
2700+/*
2701+ * Copyright (C) 2013,2014 Canonical, Ltd.
2702+ *
2703+ * This program is free software; you can redistribute it and/or modify
2704+ * it under the terms of the GNU General Public License as published by
2705+ * the Free Software Foundation; version 3.
2706+ *
2707+ * This program is distributed in the hope that it will be useful,
2708+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2709+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2710+ * GNU General Public License for more details.
2711+ *
2712+ * You should have received a copy of the GNU General Public License
2713+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2714+ */
2715+
2716+import QtQuick 2.0
2717+import QtTest 1.0
2718+import AccountsService 0.1
2719+import LightDM 0.1 as LightDM
2720+import Ubuntu.Components 1.1
2721+import Unity.Application 0.1
2722+import Unity.Test 0.1 as UT
2723+
2724+import "../../../qml"
2725+
2726+Item {
2727+ id: root
2728+ width: shellLoader.width + buttons.width
2729+ height: shellLoader.height
2730+
2731+ QtObject {
2732+ id: applicationArguments
2733+
2734+ function hasGeometry() {
2735+ return false;
2736+ }
2737+
2738+ function width() {
2739+ return 0;
2740+ }
2741+
2742+ function height() {
2743+ return 0;
2744+ }
2745+ }
2746+
2747+ Component.onCompleted: {
2748+ // must set the mock mode before loading the Shell
2749+ LightDM.Greeter.mockMode = "single-pin";
2750+ LightDM.Users.mockMode = "single-pin";
2751+ shellLoader.active = true;
2752+ }
2753+
2754+ Row {
2755+ spacing: 0
2756+ anchors.fill: parent
2757+
2758+ Loader {
2759+ id: shellLoader
2760+
2761+ active: false
2762+ width: units.gu(40)
2763+ height: units.gu(71)
2764+
2765+ property bool itemDestroyed: false
2766+ sourceComponent: Component {
2767+ Shell {
2768+ property string indicatorProfile: "phone"
2769+
2770+ Component.onDestruction: {
2771+ shellLoader.itemDestroyed = true;
2772+ }
2773+ }
2774+ }
2775+ }
2776+
2777+ Rectangle {
2778+ id: buttons
2779+ color: "white"
2780+ width: units.gu(30)
2781+ height: shellLoader.height
2782+
2783+ Column {
2784+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
2785+ spacing: units.gu(1)
2786+ Row {
2787+ anchors { left: parent.left; right: parent.right }
2788+ Button {
2789+ text: "Restart Tutorial"
2790+ onClicked: {
2791+ if (shellLoader.status !== Loader.Ready)
2792+ return;
2793+
2794+ AccountsService.demoEdges = false;
2795+ AccountsService.demoEdges = true;
2796+ }
2797+ }
2798+ }
2799+ }
2800+ }
2801+ }
2802+
2803+ UT.UnityTestCase {
2804+ id: testCase
2805+ name: "Tutorial"
2806+ when: windowShown
2807+
2808+ property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
2809+ property real halfWidth: shell ? shell.width / 2 : 0
2810+ property real halfHeight: shell ? shell.height / 2 : 0
2811+
2812+ function init() {
2813+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
2814+ swipeAwayGreeter();
2815+ AccountsService.demoEdges = false;
2816+ AccountsService.demoEdges = true;
2817+ }
2818+
2819+ function cleanup() {
2820+ shellLoader.itemDestroyed = false;
2821+
2822+ shellLoader.active = false;
2823+
2824+ tryCompare(shellLoader, "status", Loader.Null);
2825+ tryCompare(shellLoader, "item", null);
2826+ // Loader.status might be Loader.Null and Loader.item might be null but the Loader
2827+ // item might still be alive. So if we set Loader.active back to true
2828+ // again right now we will get the very same Shell instance back. So no reload
2829+ // actually took place. Likely because Loader waits until the next event loop
2830+ // iteration to do its work. So to ensure the reload, we will wait until the
2831+ // Shell instance gets destroyed.
2832+ tryCompare(shellLoader, "itemDestroyed", true);
2833+
2834+ // kill all (fake) running apps
2835+ killApps();
2836+
2837+ // reload our test subject to get it in a fresh state once again
2838+ shellLoader.active = true;
2839+
2840+ tryCompare(shellLoader, "status", Loader.Ready);
2841+ removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
2842+ }
2843+
2844+ function killApps() {
2845+ while (ApplicationManager.count > 1) {
2846+ var appIndex = ApplicationManager.get(0).appId == "unity8-dash" ? 1 : 0
2847+ ApplicationManager.stopApplication(ApplicationManager.get(appIndex).appId);
2848+ }
2849+ compare(ApplicationManager.count, 1)
2850+ }
2851+
2852+ function swipeAwayGreeter() {
2853+ var greeter = findChild(shell, "greeter");
2854+ tryCompare(greeter, "showProgress", 1);
2855+
2856+ touchFlick(shell, halfWidth, halfHeight, shell.width, halfHeight);
2857+
2858+ // wait until the animation has finished
2859+ tryCompare(greeter, "showProgress", 0);
2860+ waitForRendering(greeter);
2861+ }
2862+
2863+ function waitForPage(name) {
2864+ tryCompareFunction(function() { return findChild(shell, name) !== null; }, true);
2865+ waitForRendering(findChild(shell, name));
2866+ var page = findChild(shell, name);
2867+ tryCompare(page, "shown", true);
2868+ tryCompare(page.showAnimation, "running", false);
2869+ return page;
2870+ }
2871+
2872+ function checkTopEdge() {
2873+ touchFlick(shell, halfWidth, 0, halfWidth, halfHeight);
2874+
2875+ var panel = findChild(shell, "panel");
2876+ tryCompare(panel.indicators, "fullyClosed", true);
2877+ }
2878+
2879+ function checkLeftEdge() {
2880+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
2881+
2882+ var launcher = findChild(shell, "launcher");
2883+ tryCompare(launcher, "state", "");
2884+ }
2885+
2886+ function checkRightEdge() {
2887+ touchFlick(shell, shell.width, halfHeight, halfWidth, halfHeight);
2888+
2889+ var stage = findChild(shell, "stage");
2890+ var spreadView = findChild(stage, "spreadView");
2891+ tryCompare(spreadView, "phase", 0);
2892+ }
2893+
2894+ function checkBottomEdge() {
2895+ // Can't actually check effect of swipe, since dash isn't really loaded
2896+ var applicationsDisplayLoader = findChild(shell, "applicationsDisplayLoader");
2897+ tryCompare(applicationsDisplayLoader.item, "interactive", false);
2898+ }
2899+
2900+ function checkFinished() {
2901+ tryCompare(AccountsService, "demoEdges", false);
2902+
2903+ var tutorial = findChild(shell, "tutorial");
2904+ tryCompare(tutorial, "running", false);
2905+
2906+ var launcher = findChild(shell, "launcher");
2907+ tryCompare(launcher, "shown", false);
2908+ }
2909+
2910+ function goToPage(name) {
2911+ var page = waitForPage("tutorialLeft");
2912+ checkTopEdge();
2913+ checkRightEdge();
2914+ checkBottomEdge();
2915+ if (name === "tutorialLeft") return page;
2916+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight);
2917+
2918+ page = waitForPage("tutorialLeftFinish");
2919+ if (name === "tutorialLeftFinish") return page;
2920+ var tick = findChild(page, "tick");
2921+ tap(tick);
2922+
2923+ checkFinished();
2924+ return null;
2925+ }
2926+
2927+ function test_walkthrough() {
2928+ goToPage(null);
2929+ }
2930+
2931+ function test_launcherShortDrag() {
2932+ // goToPage does a normal launcher pull. But here we want to test
2933+ // just barely pulling the launcher out and letting go (i.e. not
2934+ // triggering the "progress" property of Launcher).
2935+
2936+ var left = goToPage("tutorialLeft");
2937+
2938+ // Make sure we don't do anything if we don't pull the launcher
2939+ // out much.
2940+ var launcher = findChild(shell, "launcher");
2941+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.4, halfHeight);
2942+ tryCompare(launcher, "state", ""); // should remain hidden
2943+ tryCompare(left, "shown", true); // and we should still be on left
2944+
2945+ // Now drag out but not past launcher itself
2946+ touchFlick(shell, 0, halfHeight, launcher.panelWidth * 0.9, halfHeight);
2947+
2948+ waitForPage("tutorialLeftFinish");
2949+ }
2950+
2951+ function test_launcherLongDrag() {
2952+ // goToPage does a normal launcher pull. But here we want to test
2953+ // a full pull across the page.
2954+
2955+ var left = goToPage("tutorialLeft");
2956+
2957+ var launcher = findChild(shell, "launcher");
2958+ touchFlick(shell, 0, halfHeight, shell.width, halfHeight);
2959+
2960+ var errorTextLabel = findChild(left, "errorTextLabel");
2961+ var errorTitleLabel = findChild(left, "errorTitleLabel");
2962+ tryCompare(launcher, "state", ""); // launcher goes away
2963+ tryCompare(left, "shown", true); // still on left page
2964+ tryCompare(errorTextLabel, "opacity", 1); // show error
2965+ tryCompare(errorTitleLabel, "opacity", 1); // show error
2966+ }
2967+
2968+ function test_launcherDragBack() {
2969+ // goToPage does a full launcher pull. But here we test pulling
2970+ // all the way out, then dragging back into place.
2971+
2972+ var left = goToPage("tutorialLeft");
2973+ touchFlick(shell, 0, halfHeight, halfWidth, halfHeight, true, false);
2974+ touchFlick(shell, halfWidth, halfHeight, 0, halfHeight, false, true);
2975+
2976+ tryCompare(left, "shown", true); // and we should still be on left
2977+ }
2978+
2979+ function test_interrupted() {
2980+ goToPage("tutorialLeft");
2981+ ApplicationManager.startApplication("dialer-app");
2982+ checkFinished();
2983+ }
2984+ }
2985+}
2986
2987=== modified file 'tests/qmltests/tst_Shell.qml'
2988--- tests/qmltests/tst_Shell.qml 2015-01-20 11:50:19 +0000
2989+++ tests/qmltests/tst_Shell.qml 2015-02-11 16:31:26 +0000
2990@@ -63,6 +63,8 @@
2991 anchors.fill: parent
2992 Loader {
2993 id: shellLoader
2994+ focus: true
2995+
2996 active: false
2997 property bool itemDestroyed: false
2998 sourceComponent: Component {
2999@@ -88,6 +90,7 @@
3000 anchors { left: parent.left; right: parent.right }
3001 Button {
3002 text: "Show Greeter"
3003+ activeFocusOnPress: false
3004 onClicked: {
3005 if (shellLoader.status !== Loader.Ready)
3006 return;
3007@@ -434,11 +437,11 @@
3008 waitUntilTransitionsEnd(appWindowStateGroup);
3009 }
3010
3011- function test_surfaceLosesFocusWhilePanelIsOpen() {
3012+ function test_surfaceLosesActiveFocusWhilePanelIsOpen() {
3013 var app = ApplicationManager.startApplication("dialer-app");
3014 waitUntilAppWindowIsFullyLoaded(app);
3015
3016- tryCompare(app.session.surface, "focus", true);
3017+ tryCompare(app.session.surface, "activeFocus", true);
3018
3019 // Drag the indicators panel half-open
3020 var touchX = shell.width / 2;
3021@@ -449,7 +452,7 @@
3022 true /* beginTouch */, false /* endTouch */);
3023 verify(indicators.partiallyOpened);
3024
3025- tryCompare(app.session.surface, "focus", false);
3026+ tryCompare(app.session.surface, "activeFocus", false);
3027
3028 // And finish getting it open
3029 touchFlick(indicators,
3030@@ -458,11 +461,21 @@
3031 false /* beginTouch */, true /* endTouch */);
3032 tryCompare(indicators, "fullyOpened", true);
3033
3034- tryCompare(app.session.surface, "focus", false);
3035+ tryCompare(app.session.surface, "activeFocus", false);
3036
3037 dragToCloseIndicatorsPanel();
3038
3039- tryCompare(app.session.surface, "focus", true);
3040+ tryCompare(app.session.surface, "activeFocus", true);
3041+ }
3042+
3043+ function test_launchedAppHasActiveFocus() {
3044+ var dialerApp = ApplicationManager.startApplication("dialer-app");
3045+ verify(dialerApp);
3046+ waitUntilAppSurfaceShowsUp("dialer-app")
3047+
3048+ verify(dialerApp.session.surface);
3049+
3050+ tryCompare(dialerApp.session.surface, "activeFocus", true);
3051 }
3052
3053 // Wait for the whole UI to settle down
3054@@ -477,6 +490,14 @@
3055 waitForRendering(shell)
3056 }
3057
3058+ function waitUntilAppSurfaceShowsUp(appId) {
3059+ var appWindow = findChild(shell, "appWindow_" + appId);
3060+ verify(appWindow);
3061+ var appWindowStates = findInvisibleChild(appWindow, "applicationWindowStateGroup");
3062+ verify(appWindowStates);
3063+ tryCompare(appWindowStates, "state", "surface");
3064+ }
3065+
3066 function dragToCloseIndicatorsPanel() {
3067 var indicators = findChild(shell, "indicators");
3068
3069
3070=== modified file 'tests/qmltests/tst_TabletShell.qml'
3071--- tests/qmltests/tst_TabletShell.qml 2015-01-20 11:50:19 +0000
3072+++ tests/qmltests/tst_TabletShell.qml 2015-02-11 16:31:26 +0000
3073@@ -303,8 +303,8 @@
3074 selectUser(data.user)
3075
3076 AccountsService.demoEdges = data.demo
3077- var edgeDemo = findChild(shell, "edgeDemo")
3078- tryCompare(edgeDemo, "running", data.demo)
3079+ var tutorial = findChild(shell, "tutorial");
3080+ tryCompare(tutorial, "running", data.demo);
3081
3082 swipeFromLeftEdge(shell.width * 0.75)
3083 wait(500) // to give time to handle dash() signal from Launcher
3084
3085=== added file 'tests/uqmlscene/ActiveFocusLogger.cpp'
3086--- tests/uqmlscene/ActiveFocusLogger.cpp 1970-01-01 00:00:00 +0000
3087+++ tests/uqmlscene/ActiveFocusLogger.cpp 2015-02-11 16:31:26 +0000
3088@@ -0,0 +1,48 @@
3089+/*
3090+ * Copyright (C) 2015 Canonical, Ltd.
3091+ *
3092+ * This program is free software; you can redistribute it and/or modify
3093+ * it under the terms of the GNU General Public License as published by
3094+ * the Free Software Foundation; version 3.
3095+ *
3096+ * This program is distributed in the hope that it will be useful,
3097+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3098+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3099+ * GNU General Public License for more details.
3100+ *
3101+ * You should have received a copy of the GNU General Public License
3102+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3103+ */
3104+
3105+#include "ActiveFocusLogger.h"
3106+
3107+#include <QDebug>
3108+#include <QQuickItem>
3109+
3110+void ActiveFocusLogger::setWindow(QQuickWindow *window)
3111+{
3112+ m_window = window;
3113+ QObject::connect(window, &QQuickWindow::activeFocusItemChanged,
3114+ this, &ActiveFocusLogger::printActiveFocusInfo);
3115+}
3116+
3117+void ActiveFocusLogger::printActiveFocusInfo()
3118+{
3119+ if (!m_window) {
3120+ return;
3121+ }
3122+
3123+ qDebug() << "============== Active focus info START ================";
3124+ if (m_window->activeFocusItem()) {
3125+ qDebug() << m_window->activeFocusItem();
3126+ qDebug() << "Ancestry:";
3127+ QQuickItem *item = m_window->activeFocusItem()->parentItem();
3128+ while (item != nullptr) {
3129+ qDebug() << item << ", isFocusScope =" << item->isFocusScope();
3130+ item = item->parentItem();
3131+ }
3132+ } else {
3133+ qDebug() << "NULL";
3134+ }
3135+ qDebug() << "============== Active focus info END ================";
3136+}
3137
3138=== added file 'tests/uqmlscene/ActiveFocusLogger.h'
3139--- tests/uqmlscene/ActiveFocusLogger.h 1970-01-01 00:00:00 +0000
3140+++ tests/uqmlscene/ActiveFocusLogger.h 2015-02-11 16:31:26 +0000
3141@@ -0,0 +1,37 @@
3142+/*
3143+ * Copyright (C) 2015 Canonical, Ltd.
3144+ *
3145+ * This program is free software; you can redistribute it and/or modify
3146+ * it under the terms of the GNU General Public License as published by
3147+ * the Free Software Foundation; version 3.
3148+ *
3149+ * This program is distributed in the hope that it will be useful,
3150+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3151+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3152+ * GNU General Public License for more details.
3153+ *
3154+ * You should have received a copy of the GNU General Public License
3155+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3156+ */
3157+
3158+#ifndef ACTIVE_FOCUS_LOGGER_H
3159+#define ACTIVE_FOCUS_LOGGER_H
3160+
3161+#include <QObject>
3162+#include <QQuickWindow>
3163+#include <QPointer>
3164+
3165+class ActiveFocusLogger : public QObject {
3166+ Q_OBJECT
3167+
3168+public:
3169+ void setWindow(QQuickWindow *window);
3170+
3171+private Q_SLOTS:
3172+ void printActiveFocusInfo();
3173+
3174+private:
3175+ QPointer<QQuickWindow> m_window;
3176+};
3177+
3178+#endif // ACTIVE_FOCUS_LOGGER_H
3179
3180=== modified file 'tests/uqmlscene/CMakeLists.txt'
3181--- tests/uqmlscene/CMakeLists.txt 2014-10-01 13:20:32 +0000
3182+++ tests/uqmlscene/CMakeLists.txt 2015-02-11 16:31:26 +0000
3183@@ -1,5 +1,6 @@
3184 add_executable(uqmlscene
3185 ${shellapplication_MOC_SRCS}
3186+ ActiveFocusLogger.cpp
3187 main.cpp
3188 ${CMAKE_SOURCE_DIR}/src/MouseTouchAdaptor.cpp
3189 )
3190
3191=== modified file 'tests/uqmlscene/main.cpp'
3192--- tests/uqmlscene/main.cpp 2014-10-17 14:22:40 +0000
3193+++ tests/uqmlscene/main.cpp 2015-02-11 16:31:26 +0000
3194@@ -68,6 +68,12 @@
3195 // UbuntuGestures lib
3196 #include <TouchRegistry.h>
3197
3198+#define UQMLSCENE_DEBUG_ACTIVE_FOCUS 0
3199+
3200+#if UQMLSCENE_DEBUG_ACTIVE_FOCUS
3201+ #include "ActiveFocusLogger.h"
3202+#endif
3203+
3204 #ifdef QML_RUNTIME_TESTING
3205 class RenderStatistics
3206 {
3207@@ -378,6 +384,10 @@
3208 {
3209 Options options;
3210
3211+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
3212+ ActiveFocusLogger activeFocusLogger;
3213+ #endif
3214+
3215 QStringList imports;
3216 QList<QPair<QString, QString> > bundles;
3217 for (int i = 1; i < argc; ++i) {
3218@@ -503,6 +513,9 @@
3219 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
3220 if (contentItem) {
3221 qxView = new QQuickView(&engine, nullptr);
3222+ #if UQMLSCENE_DEBUG_ACTIVE_FOCUS
3223+ activeFocusLogger.setWindow(qxView);
3224+ #endif
3225 TouchRegistry::instance()->setParent(qxView);
3226 qxView->installEventFilter(TouchRegistry::instance());
3227 window = qxView;

Subscribers

People subscribed via source and target branches