on xenial help sign in
Merge lp:unity8
28836cbAlbert Astals Cid
debian/changelog+24−31
@@ -1,47 +1,40 @@
1−unity8 (8.10+15.10.20150616.1-0ubuntu1) wily; urgency=medium
1+unity8 (8.10+15.10.20150618-0ubuntu1) wily; urgency=medium
2
2
3 [ Albert Astals Cid ]
3 [ Albert Astals Cid ]
4− * Bind running to the proper object.visible
4+ * Refactor QmlTest.cmake module so that all tests can go through it.
5− * Do not use template in generated card creator code
5+ Also a bit of cleanup around tests.
6− * Fix regression introduced by the concierge change
6+ * Support fallback images for dash card and preview widgets (LP:
7− * Take into account the extra margin on top when calculating the
7+ #1324142)
8− display margin
9− * workaround oxide 1.7 issue (LP: #1459142)
10
8
11 [ CI Train Bot ]
9 [ CI Train Bot ]
12 * New rebuild forced.
10 * New rebuild forced.
11+ * Resync trunk.
13
12
14− [ Federico Gimenez ]
13+ [ Daniel d'Andrada ]
15− * On the autopilot helpers, swipe if the item to open from the dash is
14+ * Fix Shell tests
16− not visible. unity8-autopilot dependencies now require ubuntu-ui-
17− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
18−
19− [ Florian Boucault ]
20− * Notifications: shaped icon was stretching the icon instead of
21− respecting its aspect ratio and filling the whole shape. (LP:
22− #1450229)
23
15
24 [ Josh Arenson ]
16 [ Josh Arenson ]
25− * Implement bare-bones shellMode=greeter and shellMode=shell
17+ * Refactor greeter emulator to unlock the greeter via dbus
26
18
27− [ Leo Arias ]
19+ [ Lukáš Tinkl ]
28− * On the autopilot helpers, swipe if the item to open from the dash is
20+ * respect target window's devicePixelRatio in MouseTouchAdaptor
29− not visible. unity8-autopilot dependencies now require ubuntu-ui-
30− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
31−
32− [ Leonardo Arias Fonseca ]
33− * On the autopilot helpers, swipe if the item to open from the dash is
34− not visible. unity8-autopilot dependencies now require ubuntu-ui-
35− toolkit-autopilot (>= 1.2.1485+15.04.20150417.1-0ubuntu1)
36
21
37 [ Michael Zanetti ]
22 [ Michael Zanetti ]
38− * add info on how to run a single function in make test
23+ * Refactor QmlTest.cmake module so that all tests can go through it.
24+ Also a bit of cleanup around tests.
25+
26+ [ Michał Sawicz ]
27+ * Refactor QmlTest.cmake module so that all tests can go through it.
28+ Also a bit of cleanup around tests.
39
29
40− [ Richard Huddie ]
30+ [ handsome_feng ]
41− * Slow down the speed of autopilot helpers used to swipe to a
31+ * Forbid closing apps during the edge gesture. (LP: #1445572)
42− different scope to improve the reliability. (LP: #1461059, #1457047)
32+ * Removed the horizonal rule on pin unlock screen. (LP: #1368798)
33+
34+ [ handsome_feng<445865575@qq.com> ]
35+ * Forbid closing apps during the edge gesture. (LP: #1445572)
43
36
44− -- CI Train Bot <ci-train-bot@canonical.com> Tue, 16 Jun 2015 15:12:59 +0000
37+ -- CI Train Bot <ci-train-bot@canonical.com> Thu, 18 Jun 2015 19:40:06 +0000
45
38
46unity8 (8.10+15.10.20150612-0ubuntu2) wily; urgency=medium
39unity8 (8.10+15.10.20150612-0ubuntu2) wily; urgency=medium
47
40
plugins/Dash/CardCreator.js+8
@@ -389,6 +389,10 @@ function cardString(template, components) {
389 }
389 }
390
390
391 code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode);
391 code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode);
392+ var fallback = components["art"] && components["art"]["fallback"] || "";
393+ if (fallback !== "") {
394+ code += 'Connections { target: artShapeLoader.item ? artShapeLoader.item.image : null; onStatusChanged: if (artShapeLoader.item.image.status === Image.Error) artShapeLoader.item.image.source = "%1"; } \n'.arg(fallback);
395+ }
392 } else {
396 } else {
393 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
397 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
394 }
398 }
@@ -468,6 +472,10 @@ function cardString(template, components) {
468
472
469 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
473 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
470 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
474 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
475+ var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
476+ if (fallback !== "") {
477+ code += 'Connections { target: mascotImage.image; onStatusChanged: if (mascotImage.image.status === Image.Error) mascotImage.source = "%1"; } \n'.arg(fallback);
478+ }
471 }
479 }
472
480
473 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
481 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? Theme.palette.normal.baseText : "white")';
po/de.po+2−2
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-06-18 05:07+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:32+0000\n"
19−"X-Generator: Launchpad (build 17570)\n"
19+"X-Generator: Launchpad (build 17572)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
po/el.po+11−11
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-04-22 09:03+0000\n"
11+"PO-Revision-Date: 2015-06-18 19:05+0000\n"
12"Last-Translator: Simos Xenitellis <Unknown>\n"
12"Last-Translator: Simos Xenitellis <Unknown>\n"
13"Language-Team: Greek <el@li.org>\n"
13"Language-Team: Greek <el@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-04-23 06:48+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19−"X-Generator: Launchpad (build 17430)\n"
19+"X-Generator: Launchpad (build 17572)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -117,7 +117,7 @@ msgstr "Ναι"
117#: qml/Components/Dialogs.qml:152
117#: qml/Components/Dialogs.qml:152
118msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
119msgid "Power"
119msgid "Power"
120−msgstr ""
120+msgstr "Τερματισμός"
121
121
122#: qml/Components/Dialogs.qml:153
122#: qml/Components/Dialogs.qml:153
123msgid ""
123msgid ""
@@ -165,17 +165,17 @@ msgstr "Αναζήτηση"
165#: qml/Dash/PageHeader.qml:269
165#: qml/Dash/PageHeader.qml:269
166msgctxt "Button: Open the Ubuntu Store"
166msgctxt "Button: Open the Ubuntu Store"
167msgid "Store"
167msgid "Store"
168−msgstr ""
168+msgstr "Κατάστημα"
169
169
170#: qml/Dash/PageHeader.qml:276
170#: qml/Dash/PageHeader.qml:276
171msgctxt "Button: Start a search in the current dash scope"
171msgctxt "Button: Start a search in the current dash scope"
172msgid "Search"
172msgid "Search"
173−msgstr ""
173+msgstr "Αναζήτηση"
174
174
175#: qml/Dash/PageHeader.qml:286
175#: qml/Dash/PageHeader.qml:286
176msgctxt "Button: Show the current dash scope settings"
176msgctxt "Button: Show the current dash scope settings"
177msgid "Settings"
177msgid "Settings"
178−msgstr ""
178+msgstr "Ρυθμίσεις"
179
179
180#: qml/Dash/PageHeader.qml:293
180#: qml/Dash/PageHeader.qml:293
181msgid "Remove from Favorites"
181msgid "Remove from Favorites"
@@ -378,7 +378,7 @@ msgstr "Πατήστε εδώ για συνέχεια."
378
378
379#: qml/Tutorial/TutorialContent.qml:81
379#: qml/Tutorial/TutorialContent.qml:81
380msgid "Click here to finish."
380msgid "Click here to finish."
381−msgstr ""
381+msgstr "Πατήστε εδώ για τέλος."
382
382
383#: qml/Tutorial/TutorialLeftFinish.qml:24
383#: qml/Tutorial/TutorialLeftFinish.qml:24
384msgid "These are the shortcuts to favorite apps"
384msgid "These are the shortcuts to favorite apps"
@@ -510,12 +510,12 @@ msgstr "Επιτρέψετε το τηλέφωνο να εντοπίσει τη
510
510
511#: qml/Wizard/Pages/50-location.qml:69
511#: qml/Wizard/Pages/50-location.qml:69
512msgid "Using GPS only (less accurate)"
512msgid "Using GPS only (less accurate)"
513−msgstr "Χρήση μόνο GPS (λιγότερο ακριβή)"
513+msgstr "Χρήση μόνο GPS (λιγότερο ακριβής)"
514
514
515#: qml/Wizard/Pages/50-location.qml:86
515#: qml/Wizard/Pages/50-location.qml:86
516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
516msgid "Using GPS, anonymized Wi-Fi and cellular network info (recommended)"
517msgstr ""
517msgstr ""
518−"Χρήση GPS, ανωνυνοποιημένα δίκτυα Wi-Fi και πληροφορίες δικτύων κινητής "
518+"Χρήση GPS, ανωνυμοποιημένων δικτύων Wi-Fi και πληροφορίες δικτύων κινητής "
519"τηλεφωνίας (συνιστάται)"
519"τηλεφωνίας (συνιστάται)"
520
520
521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
521#. TRANSLATORS: HERE is a trademark for Nokia's location service, you probably shouldn't translate it
@@ -546,7 +546,7 @@ msgid ""
546"partners, the makers of the operating system."
546"partners, the makers of the operating system."
547msgstr ""
547msgstr ""
548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
548"Το τηλέφωνό σας είναι ρυθμισμένο να στέλνει αυτόματα αναφορές σφαλμάτων στην "
549−"Canonical και τους συνεργάτες της, τους δημιουργούς τους λειτουργικού "
549+"Canonical και τους συνεργάτες της, τους δημιουργούς του λειτουργικού "
550"συστήματος."
550"συστήματος."
551
551
552#: qml/Wizard/Pages/60-reporting.qml:43
552#: qml/Wizard/Pages/60-reporting.qml:43
po/hu.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
10"POT-Creation-Date: 2015-04-14 16:13+0200\n"
11−"PO-Revision-Date: 2015-05-04 18:02+0000\n"
11+"PO-Revision-Date: 2015-06-19 09:54+0000\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
12"Last-Translator: Richard Somlói <ricsipontaz@gmail.com>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
13"Language-Team: Hungarian <hu@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-05-05 05:47+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19−"X-Generator: Launchpad (build 17474)\n"
19+"X-Generator: Launchpad (build 17572)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -113,7 +113,7 @@ msgstr "Igen"
113#: qml/Components/Dialogs.qml:152
113#: qml/Components/Dialogs.qml:152
114msgctxt "Title: Power off/Restart dialog"
114msgctxt "Title: Power off/Restart dialog"
115msgid "Power"
115msgid "Power"
116−msgstr "Főkapcsoló"
116+msgstr "Menü"
117
117
118#: qml/Components/Dialogs.qml:153
118#: qml/Components/Dialogs.qml:153
119msgid ""
119msgid ""
po/it.po+7−7
@@ -15,8 +15,8 @@ msgstr ""
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2015-06-18 05:07+0000\n"
18+"X-Launchpad-Export-Date: 2015-06-23 05:33+0000\n"
19−"X-Generator: Launchpad (build 17570)\n"
19+"X-Generator: Launchpad (build 17572)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:112
21#: plugins/LightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -640,11 +640,6 @@ msgstr "%1 〉"
640#~ msgstr ""
640#~ msgstr ""
641#~ "Provare a far scorrere dal bordo in alto per accedere agli indicatori"
641#~ "Provare a far scorrere dal bordo in alto per accedere agli indicatori"
642
642
643−#~ msgid "Swipe up again to close the settings screen"
644−#~ msgstr ""
645−#~ "Far scorrere verso l'alto nuovamente per chiudere la schermata delle "
646−#~ "impostazioni"
647−
648#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
643#~ msgid "Swipe from the left to reveal the launcher for quick access to apps"
649#~ msgstr ""
644#~ msgstr ""
650#~ "Far scorrere il bordo sinistro per mostrare il Launcher e avere un rapido "
645#~ "Far scorrere il bordo sinistro per mostrare il Launcher e avere un rapido "
@@ -698,3 +693,8 @@ msgstr "%1 〉"
698
693
699#~ msgid "Manage Scopes"
694#~ msgid "Manage Scopes"
700#~ msgstr "Gestisci Scope"
695#~ msgstr "Gestisci Scope"
696+
697+#~ msgid "Swipe up again to close the settings screen"
698+#~ msgstr ""
699+#~ "Far scorrere nuovamente verso l'alto per chiudere la schermata delle "
700+#~ "impostazioni"
qml/Components/PinLockscreen.qml+1−5
@@ -52,7 +52,7 @@ Item {
52 verticalCenter: parent.verticalCenter;
52 verticalCenter: parent.verticalCenter;
53 verticalCenterOffset: Math.max(-units.gu(10), -(root.height - height) / 2) + units.gu(4)
53 verticalCenterOffset: Math.max(-units.gu(10), -(root.height - height) / 2) + units.gu(4)
54 }
54 }
55− spacing: units.gu(2)
55+ spacing: units.gu(4)
56
56
57 Column {
57 Column {
58 id: shakeContainer
58 id: shakeContainer
@@ -154,10 +154,6 @@ Item {
154 }
154 }
155 }
155 }
156
156
157− ThinDivider {
158− anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
159− }
160−
161 Grid {
157 Grid {
162 id: numbersGrid
158 id: numbersGrid
163 objectName: "numbersGrid"
159 objectName: "numbersGrid"
qml/Components/ZoomableImage.qml+1
@@ -30,6 +30,7 @@ Item {
30 property var zoomable: false
30 property var zoomable: false
31 property alias imageStatus: imageRenderer.status
31 property alias imageStatus: imageRenderer.status
32 property alias asynchronous: imageRenderer.asynchronous
32 property alias asynchronous: imageRenderer.asynchronous
33+ readonly property alias status: imageRenderer.status
33
34
34 Flickable {
35 Flickable {
35 id: flickable
36 id: flickable
qml/Dash/Previews/PreviewHeader.qml+6
@@ -22,6 +22,7 @@ import "../"
22/*! This preview widget shows a header
22/*! This preview widget shows a header
23 * The title comes in widgetData["title"]
23 * The title comes in widgetData["title"]
24 * The mascot comes in widgetData["mascot"]
24 * The mascot comes in widgetData["mascot"]
25+ * The mascot fall back image comes in widgetData["fallback"]
25 * The subtitle comes in widgetData["subtitle"]
26 * The subtitle comes in widgetData["subtitle"]
26 * The attributes comes in widgetData["attributes"]
27 * The attributes comes in widgetData["attributes"]
27 */
28 */
@@ -35,11 +36,15 @@ PreviewWidget {
35 id: headerRoot
36 id: headerRoot
36 objectName: "innerPreviewHeader"
37 objectName: "innerPreviewHeader"
37 readonly property url mascot: root.widgetData["mascot"] || ""
38 readonly property url mascot: root.widgetData["mascot"] || ""
39+ readonly property url fallback: root.widgetData["fallback"] || ""
38 readonly property string title: root.widgetData["title"] || ""
40 readonly property string title: root.widgetData["title"] || ""
39 readonly property string subtitle: root.widgetData["subtitle"] || ""
41 readonly property string subtitle: root.widgetData["subtitle"] || ""
40 readonly property var attributes: root.widgetData["attributes"] || null
42 readonly property var attributes: root.widgetData["attributes"] || null
41 readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
43 readonly property color fontColor: root.scopeStyle ? root.scopeStyle.foreground : Theme.palette.normal.baseText
42
44
45+ // Rewire the source since we may have unwired it on onStatusChanged
46+ onMascotChanged: if (mascotShapeLoader.item) mascotShapeLoader.item.image.source = mascot;
47+
43 implicitHeight: row.height + row.margins * 2
48 implicitHeight: row.height + row.margins * 2
44 width: parent.width
49 width: parent.width
45
50
@@ -82,6 +87,7 @@ PreviewWidget {
82 fillMode: Image.PreserveAspectCrop
87 fillMode: Image.PreserveAspectCrop
83 horizontalAlignment: Image.AlignHCenter
88 horizontalAlignment: Image.AlignHCenter
84 verticalAlignment: Image.AlignVCenter
89 verticalAlignment: Image.AlignVCenter
90+ onStatusChanged: if (status === Image.Error) source = headerRoot.fallback;
85 }
91 }
86 }
92 }
87 }
93 }
qml/Dash/Previews/PreviewImageGallery.qml+14
@@ -20,6 +20,7 @@ import "../../Components"
20
20
21/*! This preview widget shows a horizontal list of images.
21/*! This preview widget shows a horizontal list of images.
22 * The URIs for the images should be an array in widgetData["sources"].
22 * The URIs for the images should be an array in widgetData["sources"].
23+ * Images fall back to widgetData["fallback"] if loading fails
23 */
24 */
24
25
25PreviewWidget {
26PreviewWidget {
@@ -72,6 +73,14 @@ PreviewWidget {
72 overlay.show();
73 overlay.show();
73 }
74 }
74 }
75 }
76+
77+ Connections {
78+ target: sourceImage
79+ // If modelData would change after failing to load it would not be
80+ // reloaded since the source binding is destroyed by the source = fallback
81+ // But at the moment the model never changes
82+ onStatusChanged: if (sourceImage.status === Image.Error) sourceImage.source = widgetData["fallback"];
83+ }
75 }
84 }
76 }
85 }
77
86
@@ -122,6 +131,11 @@ PreviewWidget {
122 source: modelData ? modelData : ""
131 source: modelData ? modelData : ""
123 fillMode: Image.PreserveAspectFit
132 fillMode: Image.PreserveAspectFit
124 sourceSize { width: screenshot.width; height: screenshot.height }
133 sourceSize { width: screenshot.width; height: screenshot.height }
134+
135+ // If modelData would change after failing to load it would not be
136+ // reloaded since the source binding is destroyed by the source = fallback
137+ // But at the moment the model never changes
138+ onStatusChanged: if (status === Image.Error) source = widgetData["fallback"];
125 }
139 }
126
140
127 MouseArea {
141 MouseArea {
qml/Dash/Previews/PreviewZoomableImage.qml+13
@@ -21,6 +21,7 @@ import "../../Components"
21/*! \brief Preview widget for image.
21/*! \brief Preview widget for image.
22
22
23 This widget shows image contained in widgetData["source"],
23 This widget shows image contained in widgetData["source"],
24+ and falls back to widgetData["fallback"] if loading fails
24 can be zoomable accordingly with widgetData["zoomable"].
25 can be zoomable accordingly with widgetData["zoomable"].
25 */
26 */
26
27
@@ -53,6 +54,14 @@ PreviewWidget {
53 overlay.show();
54 overlay.show();
54 }
55 }
55 }
56 }
57+
58+ Connections {
59+ target: lazyImage.sourceImage
60+ // If modelData would change after failing to load it would not be
61+ // reloaded since the source binding is destroyed by the source = fallback
62+ // But at the moment the model never changes
63+ onStatusChanged: if (lazyImage.sourceImage.status === Image.Error) lazyImage.sourceImage.source = widgetData["fallback"];
64+ }
56 }
65 }
57
66
58 PreviewOverlay {
67 PreviewOverlay {
@@ -67,6 +76,10 @@ PreviewWidget {
67 anchors.fill: parent
76 anchors.fill: parent
68 source: widgetData["source"]
77 source: widgetData["source"]
69 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
78 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
79+ // If modelData would change after failing to load it would not be
80+ // reloaded since the source binding is destroyed by the source = fallback
81+ // But at the moment the model never changes
82+ onStatusChanged: if (status === Image.Error) source = widgetData["fallback"];
70 }
83 }
71 }
84 }
72}
85}
qml/Stages/PhoneStage.qml+7
@@ -548,6 +548,13 @@ Rectangle {
548 }
548 }
549 }
549 }
550
550
551+ //eat touch events during the right edge gesture
552+ MouseArea {
553+ objectName: "eventEaterArea"
554+ anchors.fill: parent
555+ enabled: spreadDragArea.dragging
556+ }
557+
551 DirectionalDragArea {
558 DirectionalDragArea {
552 id: spreadDragArea
559 id: spreadDragArea
553 objectName: "spreadDragArea"
560 objectName: "spreadDragArea"
qml/Stages/TabletStage.qml+6
@@ -724,6 +724,12 @@ Rectangle {
724 }
724 }
725 }
725 }
726
726
727+ //eat touch events during the right edge gesture
728+ MouseArea {
729+ anchors.fill: parent
730+ enabled: spreadDragArea.dragging
731+ }
732+
727 DirectionalDragArea {
733 DirectionalDragArea {
728 id: spreadDragArea
734 id: spreadDragArea
729 objectName: "spreadDragArea"
735 objectName: "spreadDragArea"
src/MouseTouchAdaptor.cpp+6−6
@@ -104,10 +104,10 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
104 if (button != Qt::LeftButton)
104 if (button != Qt::LeftButton)
105 return true;
105 return true;
106
106
107− QPoint windowPos(pressEvent->event_x, pressEvent->event_y);
108−
109 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
107 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
110
108
109+ QPoint windowPos(pressEvent->event_x / targetWindow->devicePixelRatio(), pressEvent->event_y / targetWindow->devicePixelRatio());
110+
111 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
111 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
112 false /* autoCommit */);
112 false /* autoCommit */);
113 touchEvent.press(0 /* touchId */, windowPos);
113 touchEvent.press(0 /* touchId */, windowPos);
@@ -125,10 +125,10 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
125 if (button != Qt::LeftButton)
125 if (button != Qt::LeftButton)
126 return true;
126 return true;
127
127
128− QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y);
129−
130 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
128 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
131
129
130+ QPoint windowPos(releaseEvent->event_x / targetWindow->devicePixelRatio(), releaseEvent->event_y / targetWindow->devicePixelRatio());
131+
132 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
132 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
133 false /* autoCommit */);
133 false /* autoCommit */);
134 touchEvent.release(0 /* touchId */, windowPos);
134 touchEvent.release(0 /* touchId */, windowPos);
@@ -144,10 +144,10 @@ bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
144 return true;
144 return true;
145 }
145 }
146
146
147− QPoint windowPos(event->event_x, event->event_y);
148−
149 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
147 QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
150
148
149+ QPoint windowPos(event->event_x / targetWindow->devicePixelRatio(), event->event_y / targetWindow->devicePixelRatio());
150+
151 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
151 QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
152 false /* autoCommit */);
152 false /* autoCommit */);
153 touchEvent.move(0 /* touchId */, windowPos);
153 touchEvent.move(0 /* touchId */, windowPos);
tests/autopilot/unity8/application_lifecycle/tests/__init__.py+2−2
@@ -31,8 +31,8 @@ class ApplicationLifeCycleTestCase(tests.UnityTestCase):
31 super().setUp()
31 super().setUp()
32 self._qml_mock_enabled = False
32 self._qml_mock_enabled = False
33 self._data_dirs_mock_enabled = False
33 self._data_dirs_mock_enabled = False
34− unity_proxy = self.launch_unity()
34+ self.launch_unity()
35− process_helpers.unlock_unity(unity_proxy)
35+ process_helpers.unlock_unity()
36
36
37 def create_test_application(self):
37 def create_test_application(self):
38 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
38 desktop_file_dict = fixture_setup.DEFAULT_DESKTOP_FILE_DICT
tests/autopilot/unity8/greeter/__init__.py+52
@@ -0,0 +1,52 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2015 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+import dbus
21+
22+from autopilot.matchers import Eventually
23+from testtools.matchers import Equals
24+
25+
26+def hide_greeter_with_dbus():
27+ dbus_proxy = _get_greeter_dbus_proxy()
28+ if _is_greeter_active():
29+ dbus_proxy.HideGreeter()
30+
31+
32+def show_greeter_with_dbus():
33+ dbus_proxy = _get_greeter_dbus_proxy()
34+ if not _is_greeter_active():
35+ dbus_proxy.ShowGreeter()
36+
37+
38+def wait_for_greeter():
39+ Eventually(Equals(True), timeout=300).match(_is_greeter_active)
40+
41+
42+def _get_greeter_dbus_proxy():
43+ bus = dbus.SessionBus()
44+ return bus.get_object('com.canonical.UnityGreeter', '/')
45+
46+
47+def _is_greeter_active():
48+ try:
49+ dbus_proxy = _get_greeter_dbus_proxy()
50+ return dbus_proxy.Get('com.canonical.UnityGreeter', 'IsActive')
51+ except:
52+ return False
tests/autopilot/unity8/indicators/tests/__init__.py+1−1
@@ -31,7 +31,7 @@ class IndicatorTestCase(tests.UnityTestCase):
31 def setUp(self):
31 def setUp(self):
32 super().setUp()
32 super().setUp()
33 self.unity_proxy = self.launch_unity()
33 self.unity_proxy = self.launch_unity()
34− process_helpers.unlock_unity(self.unity_proxy)
34+ process_helpers.unlock_unity()
35
35
36
36
37class DeviceIndicatorTestCase(IndicatorTestCase):
37class DeviceIndicatorTestCase(IndicatorTestCase):
tests/autopilot/unity8/process_helpers.py+7−94
@@ -19,14 +19,11 @@
19
19
20import logging
20import logging
21import subprocess
21import subprocess
22−import dbus
23
22
24import ubuntuuitoolkit
23import ubuntuuitoolkit
25−from autopilot.exceptions import ProcessSearchError
26from autopilot.introspection import get_proxy_object_for_existing_process
24from autopilot.introspection import get_proxy_object_for_existing_process
27
25
28−from unity8.shell import emulators
26+from unity8 import greeter
29−from unity8.shell.emulators import main_window as main_window_emulator
30
27
31logger = logging.getLogger(__name__)
28logger = logging.getLogger(__name__)
32
29
@@ -39,104 +36,20 @@ class CannotAccessUnity(Exception):
39 pass
36 pass
40
37
41
38
42−def unlock_unity(unity_proxy_obj=None):
39+def unlock_unity():
43 """Helper function that attempts to unlock the unity greeter.
40 """Helper function that attempts to unlock the unity greeter.
44
41
45− If unity_proxy_object is None create a proxy object by querying for the
46− running unity process.
47− Otherwise re-use the passed proxy object.
48−
49− :raises RuntimeError: if the greeter attempts and fails to be unlocked.
50−
51− :raises RuntimeWarning: when the greeter cannot be found because it is
52− already unlocked.
53− :raises CannotAccessUnity: if unity is not introspectable or cannot be
54− found on dbus.
55− :raises CannotAccessUnity: if unity's upstart status is not "start" or the
56− upstart job cannot be found at all.
57−
58 """
42 """
59− if unity_proxy_obj is None:
43+ greeter.wait_for_greeter()
60− try:
44+ greeter.hide_greeter_with_dbus()
61− pid = get_unity_pid()
62− unity = _get_unity_proxy_object(pid)
63− main_window = unity.select_single(main_window_emulator.QQuickView)
64− except ProcessSearchError as e:
65− raise CannotAccessUnity(
66− "Cannot introspect unity, make sure that it has been started "
67− "with testability. Perhaps use the function "
68− "'restart_unity_with_testability' this module provides."
69− "(%s)"
70− % str(e)
71− )
72− else:
73− main_window = unity_proxy_obj.select_single(
74− main_window_emulator.QQuickView)
75−
76− greeter = main_window.get_greeter()
77− if greeter.created is False:
78− raise RuntimeWarning("Greeter appears to be already unlocked.")
79−
80− bus = dbus.SessionBus()
81− dbus_proxy = bus.get_object("com.canonical.UnityGreeter", "/")
82− try:
83− dbus_proxy.HideGreeter()
84− except dbus.DBusException:
85− logger.info("Failed to unlock greeter")
86− raise
87− else:
88− greeter.created.wait_for(False)
89− logger.info("Greeter unlocked, continuing.")
90
45
91
46
92−def lock_unity(unity_proxy_obj=None):
47+def lock_unity():
93 """Helper function that attempts to lock unity greeter.
48 """Helper function that attempts to lock unity greeter.
94
49
95− If unity_proxy_object is None create a proxy object by querying for the
96− running unity process.
97− Otherwise re-use the passed proxy object.
98−
99− :raises RuntimeError: if the greeter attempts and fails to be locked.
100−
101− :raises RuntimeWarning: when the greeter is found because it is
102− already locked.
103− :raises CannotAccessUnity: if unity is not introspectable or cannot be
104− found on dbus.
105− :raises CannotAccessUnity: if unity's upstart status is not "start" or the
106− upstart job cannot be found at all.
107−
108 """
50 """
109− if unity_proxy_obj is None:
51+ greeter.show_greeter_with_dbus()
110− try:
52+ greeter.wait_for_greeter()
111− pid = get_unity_pid()
112− unity = _get_unity_proxy_object(pid)
113− main_window = unity.select_single(main_window_emulator.QQuickView)
114− except ProcessSearchError as e:
115− raise CannotAccessUnity(
116− "Cannot introspect unity, make sure that it has been started "
117− "with testability. Perhaps use the function "
118− "'restart_unity_with_testability' this module provides."
119− "(%s)"
120− % str(e)
121− )
122− else:
123− main_window = unity_proxy_obj.select_single(
124− main_window_emulator.QQuickView)
125−
126− greeter = main_window.get_greeter()
127− if greeter.created is True:
128− raise RuntimeWarning("Greeter appears to be already locked.")
129−
130− bus = dbus.SessionBus()
131− dbus_proxy = bus.get_object("com.canonical.UnityGreeter", "/")
132− try:
133− dbus_proxy.ShowGreeter()
134− except dbus.DBusException:
135− logger.info("Failed to lock greeter")
136− raise
137− else:
138− greeter.created.wait_for(True)
139− logger.info("Greeter locked, continuing.")
140
53
141
54
142def restart_unity_with_testability(*args):
55def restart_unity_with_testability(*args):
tests/autopilot/unity8/shell/tests/test_emulators.py+2−2
@@ -41,8 +41,8 @@ class MainWindowTestCase(tests.UnityTestCase):
41
41
42 def setUp(self):
42 def setUp(self):
43 super().setUp()
43 super().setUp()
44− unity_proxy = self.launch_unity()
44+ self.launch_unity()
45− process_helpers.unlock_unity(unity_proxy)
45+ process_helpers.unlock_unity()
46
46
47
47
48class DashEmulatorTestCase(tests.DashBaseTestCase):
48class DashEmulatorTestCase(tests.DashBaseTestCase):
tests/autopilot/unity8/shell/tests/test_notifications.py+22−22
@@ -119,8 +119,8 @@ class InteractiveNotificationBase(NotificationsBase):
119
119
120 def test_interactive(self):
120 def test_interactive(self):
121 """Interactive notification must react upon click on itself."""
121 """Interactive notification must react upon click on itself."""
122− unity_proxy = self.launch_unity()
122+ self.launch_unity()
123− unlock_unity(unity_proxy)
123+ unlock_unity()
124
124
125 notify_list = self._get_notifications_list()
125 notify_list = self._get_notifications_list()
126
126
@@ -156,8 +156,8 @@ class InteractiveNotificationBase(NotificationsBase):
156 """Snap-decision with three actions should use
156 """Snap-decision with three actions should use
157 one-over two button layout.
157 one-over two button layout.
158 """
158 """
159− unity_proxy = self.launch_unity()
159+ self.launch_unity()
160− unlock_unity(unity_proxy)
160+ unlock_unity()
161
161
162 summary = "Theatre at Ferria Stadium"
162 summary = "Theatre at Ferria Stadium"
163 body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
163 body = "at Ferria Stadium in Bilbao, Spain\n07578545317"
@@ -197,8 +197,8 @@ class InteractiveNotificationBase(NotificationsBase):
197 """Snap-decision should block input to shell
197 """Snap-decision should block input to shell
198 without greeter/lockscreen.
198 without greeter/lockscreen.
199 """
199 """
200− unity_proxy = self.launch_unity()
200+ self.launch_unity()
201− unlock_unity(unity_proxy)
201+ unlock_unity()
202
202
203 summary = "Incoming file"
203 summary = "Incoming file"
204 body = "Frank would like to send you the file: essay.pdf"
204 body = "Frank would like to send you the file: essay.pdf"
@@ -421,8 +421,8 @@ class EphemeralNotificationsTests(NotificationsBase):
421
421
422 def test_icon_summary_body(self):
422 def test_icon_summary_body(self):
423 """Notification must display the expected summary and body text."""
423 """Notification must display the expected summary and body text."""
424− unity_proxy = self.launch_unity()
424+ self.launch_unity()
425− unlock_unity(unity_proxy)
425+ unlock_unity()
426
426
427 notify_list = self._get_notifications_list()
427 notify_list = self._get_notifications_list()
428
428
@@ -458,8 +458,8 @@ class EphemeralNotificationsTests(NotificationsBase):
458 def test_icon_summary(self):
458 def test_icon_summary(self):
459 """Notification must display the expected summary and secondary
459 """Notification must display the expected summary and secondary
460 icon."""
460 icon."""
461− unity_proxy = self.launch_unity()
461+ self.launch_unity()
462− unlock_unity(unity_proxy)
462+ unlock_unity()
463
463
464 notify_list = self._get_notifications_list()
464 notify_list = self._get_notifications_list()
465
465
@@ -491,8 +491,8 @@ class EphemeralNotificationsTests(NotificationsBase):
491 def test_urgency_order(self):
491 def test_urgency_order(self):
492 """Notifications must be displayed in order according to their
492 """Notifications must be displayed in order according to their
493 urgency."""
493 urgency."""
494− unity_proxy = self.launch_unity()
494+ self.launch_unity()
495− unlock_unity(unity_proxy)
495+ unlock_unity()
496
496
497 notify_list = self._get_notifications_list()
497 notify_list = self._get_notifications_list()
498
498
@@ -578,8 +578,8 @@ class EphemeralNotificationsTests(NotificationsBase):
578
578
579 def test_summary_and_body(self):
579 def test_summary_and_body(self):
580 """Notification must display the expected summary- and body-text."""
580 """Notification must display the expected summary- and body-text."""
581− unity_proxy = self.launch_unity()
581+ self.launch_unity()
582− unlock_unity(unity_proxy)
582+ unlock_unity()
583
583
584 notify_list = self._get_notifications_list()
584 notify_list = self._get_notifications_list()
585
585
@@ -603,8 +603,8 @@ class EphemeralNotificationsTests(NotificationsBase):
603
603
604 def test_summary_only(self):
604 def test_summary_only(self):
605 """Notification must display only the expected summary-text."""
605 """Notification must display only the expected summary-text."""
606− unity_proxy = self.launch_unity()
606+ self.launch_unity()
607− unlock_unity(unity_proxy)
607+ unlock_unity()
608
608
609 notify_list = self._get_notifications_list()
609 notify_list = self._get_notifications_list()
610
610
@@ -621,8 +621,8 @@ class EphemeralNotificationsTests(NotificationsBase):
621 def test_update_notification_same_layout(self):
621 def test_update_notification_same_layout(self):
622 """Notification must allow updating its contents while being
622 """Notification must allow updating its contents while being
623 displayed."""
623 displayed."""
624− unity_proxy = self.launch_unity()
624+ self.launch_unity()
625− unlock_unity(unity_proxy)
625+ unlock_unity()
626
626
627 notify_list = self._get_notifications_list()
627 notify_list = self._get_notifications_list()
628
628
@@ -660,8 +660,8 @@ class EphemeralNotificationsTests(NotificationsBase):
660 def test_update_notification_layout_change(self):
660 def test_update_notification_layout_change(self):
661 """Notification must allow updating its contents and layout while
661 """Notification must allow updating its contents and layout while
662 being displayed."""
662 being displayed."""
663− unity_proxy = self.launch_unity()
663+ self.launch_unity()
664− unlock_unity(unity_proxy)
664+ unlock_unity()
665
665
666 notify_list = self._get_notifications_list()
666 notify_list = self._get_notifications_list()
667
667
@@ -709,8 +709,8 @@ class EphemeralNotificationsTests(NotificationsBase):
709 """ use the create notification script to get a notification dialog.
709 """ use the create notification script to get a notification dialog.
710 Check that the arguments passed to the script match the fields. """
710 Check that the arguments passed to the script match the fields. """
711
711
712− unity_proxy = self.launch_unity()
712+ self.launch_unity()
713− unlock_unity(unity_proxy)
713+ unlock_unity()
714
714
715 summary = 'Helper summary'
715 summary = 'Helper summary'
716 body = 'Helper body'
716 body = 'Helper body'
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+30
@@ -38,6 +38,22 @@ Rectangle {
38 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
38 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
39 }
39 }
40
40
41+ property var brokenheaderjson: {
42+ "title": "THE TITLE",
43+ "subtitle": "Something catchy",
44+ "mascot": "bad_path",
45+ "fallback": "",
46+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
47+ }
48+
49+ property var fallbackheaderjson: {
50+ "title": "THE TITLE",
51+ "subtitle": "Something catchy",
52+ "mascot": "bad_path2",
53+ "fallback": "../graphics/play_button.png",
54+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
55+ }
56+
41 PreviewHeader {
57 PreviewHeader {
42 id: previewHeader
58 id: previewHeader
43 widgetData: headerjson
59 widgetData: headerjson
@@ -121,5 +137,19 @@ Rectangle {
121 compare(innerPreviewHeader.subtitle, "Something catchy");
137 compare(innerPreviewHeader.subtitle, "Something catchy");
122 compare(innerPreviewHeader.mascot.toString().slice(-24), "graphics/play_button.png");
138 compare(innerPreviewHeader.mascot.toString().slice(-24), "graphics/play_button.png");
123 }
139 }
140+
141+ function test_fallback() {
142+ previewHeader.widgetData = brokenheaderjson;
143+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
144+ var mascot = findChild(previewHeader, "mascotShape");
145+ compare(mascot.visible, false);
146+
147+ previewHeader.widgetData = {};
148+ previewHeader.widgetData = fallbackheaderjson;
149+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
150+ var mascot = findChild(previewHeader, "mascotShape");
151+ tryCompare(mascot, "visible", true);
152+ tryCompare(mascot.image, "status", Image.Ready);
153+ }
124 }
154 }
125}
155}
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+21−1
@@ -33,10 +33,21 @@ Rectangle {
33 "sources": [
33 "sources": [
34 "../../graphics/phone_background.jpg",
34 "../../graphics/phone_background.jpg",
35 "../../graphics/tablet_background.jpg",
35 "../../graphics/tablet_background.jpg",
36− "../../graphics/clock@18.png"
36+ "../../graphics/clock@18.png",
37+ "../../graphics/borked"
37 ]
38 ]
38 }
39 }
39
40
41+ property var sourcesModel1WithFallback: {
42+ "sources": [
43+ "../../graphics/phone_background.jpg",
44+ "../../graphics/tablet_background.jpg",
45+ "../../graphics/clock@18.png",
46+ "../../graphics/borked"
47+ ]
48+ , "fallback": "../../graphics/clock@18.png"
49+ }
50+
40 PreviewImageGallery {
51 PreviewImageGallery {
41 id: imageGallery
52 id: imageGallery
42 width: parent.width
53 width: parent.width
@@ -103,5 +114,14 @@ Rectangle {
103 tryCompare(overlayListView, "currentIndex", data.index);
114 tryCompare(overlayListView, "currentIndex", data.index);
104 verify(image.source === overlayListView.currentItem.source);
115 verify(image.source === overlayListView.currentItem.source);
105 }
116 }
117+
118+ function test_fallback() {
119+ var image3 = findChild(imageGallery, "previewImage3");
120+ tryCompare(image3, "state", "error");
121+ imageGallery.widgetData = sourcesModel0;
122+ imageGallery.widgetData = sourcesModel1WithFallback;
123+ image3 = findChild(imageGallery, "previewImage3");
124+ tryCompare(image3, "state", "ready");
125+ }
106 }
126 }
107}
127}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+27−6
@@ -26,7 +26,7 @@ Rectangle {
26 color: "lightgrey"
26 color: "lightgrey"
27
27
28 property var widgetData0: {
28 property var widgetData0: {
29− "source": Qt.resolvedUrl("../artwork/checkers.png"),
29+ "source": "../../graphics/phone_background.jpg",
30 "zoomable": false
30 "zoomable": false
31 }
31 }
32
32
@@ -34,21 +34,35 @@ Rectangle {
34 "source": ""
34 "source": ""
35 }
35 }
36
36
37− PreviewZoomableImage {
37+ property var widgetData2: {
38− id: zoomableImage
38+ "source": "fadsasf",
39+ "fallback": "../../graphics/phone_background.jpg"
40+ }
41+
42+ Loader {
43+ id: loader
39 anchors.centerIn: parent
44 anchors.centerIn: parent
45+ sourceComponent: PreviewZoomableImage {
40− widgetData: widgetData0
46+ widgetData: widgetData0
47+ }
41 }
48 }
42
49
50+ property alias zoomableImage: loader.item
51+
43 UT.UnityTestCase {
52 UT.UnityTestCase {
44 name: "PreviewZoomableImageTest"
53 name: "PreviewZoomableImageTest"
45 when: windowShown
54 when: windowShown
46
55
47− property Item lazyImage: findChild(zoomableImage, "lazyImage");
56+ property Item lazyImage
48− property Item overlay: findChild(zoomableImage.rootItem, "overlay");
57+ property Item overlay
49
58
50 function init() {
59 function init() {
60+ // Use a loader so we start from scratch each time
61+ loader.active = false;
62+ loader.active = true;
63+ lazyImage = findChild(zoomableImage, "lazyImage");
51 waitForRendering(zoomableImage);
64 waitForRendering(zoomableImage);
65+ overlay = findChild(zoomableImage.rootItem, "overlay");
52 waitForRendering(overlay);
66 waitForRendering(overlay);
53 }
67 }
54
68
@@ -79,5 +93,12 @@ Rectangle {
79 mouseClick(overlayCloseButton);
93 mouseClick(overlayCloseButton);
80 tryCompare(overlay, "visible", false);
94 tryCompare(overlay, "visible", false);
81 }
95 }
96+
97+ function test_fallback() {
98+ zoomableImage.widgetData = widgetData2;
99+ waitForRendering(zoomableImage);
100+ waitForRendering(lazyImage);
101+ tryCompare(lazyImage, "state", "ready");
102+ }
82 }
103 }
83}
104}
tests/qmltests/Dash/tst_Card.qml+30
@@ -223,6 +223,7 @@ Rectangle {
223 property Item mascotImage: findChild(card, "mascotImage");
223 property Item mascotImage: findChild(card, "mascotImage");
224
224
225 function init() {
225 function init() {
226+ cardTool.components = Qt.binding(function() { return Helpers.update(JSON.parse(Helpers.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components']; });
226 loader.visible = true;
227 loader.visible = true;
227 }
228 }
228
229
@@ -423,6 +424,7 @@ Rectangle {
423 ];
424 ];
424 }
425 }
425
426
427+
426 function test_background(data) {
428 function test_background(data) {
427 selector.selectedIndex = data.index;
429 selector.selectedIndex = data.index;
428 waitForRendering(selector);
430 waitForRendering(selector);
@@ -453,6 +455,34 @@ Rectangle {
453 }
455 }
454 }
456 }
455
457
458+ function test_fallback() {
459+ selector.selectedIndex = 0;
460+ waitForRendering(card);
461+
462+ card.cardData["art"] = "somethingbroken";
463+ card.cardDataChanged();
464+ waitForRendering(card);
465+ tryCompare(art, "visible", false);
466+
467+ cardTool.components["art"]["fallback"] = Qt.resolvedUrl("artwork/emblem.png");
468+ cardTool.componentsChanged();
469+ card.cardData["art"] = "somethingbroken";
470+ card.cardDataChanged();
471+ waitForRendering(card);
472+ tryCompare(art, "visible", true);
473+
474+ card.cardData["mascot"] = "somethingbroken2";
475+ card.cardDataChanged();
476+ compare(mascotImage.image.status, Image.Error);
477+
478+ cardTool.components["mascot"] = {"fallback": Qt.resolvedUrl("artwork/emblem.png")};
479+ cardTool.componentsChanged();
480+ card.cardData["mascot"] = "somethingbroken2";
481+ card.cardDataChanged();
482+ waitForRendering(card);
483+ tryCompare(mascotImage.image, "status", Image.Ready);
484+ }
485+
456 function test_font_weights_data() {
486 function test_font_weights_data() {
457 return [
487 return [
458 { tag: "Title only", index: 8, weight: Font.Normal },
488 { tag: "Title only", index: 8, weight: Font.Normal },
tests/qmltests/Stages/tst_PhoneStage.qml+19
@@ -346,6 +346,25 @@ Item {
346 tryCompare(spreadView, "contentX", -spreadView.shift)
346 tryCompare(spreadView, "contentX", -spreadView.shift)
347 }
347 }
348
348
349+ function test_cantAccessPhoneStageWhileRightEdgeGesture() {
350+ var spreadView = findChild(phoneStage, "spreadView");
351+ var eventEaterArea = findChild(phoneStage, "eventEaterArea")
352+
353+ var startX = phoneStage.width - 2;
354+ var startY = phoneStage.height / 2;
355+ var endY = startY;
356+ var endX = phoneStage.width / 2;
357+
358+ touchFlick(phoneStage, startX, startY, endX, endY,
359+ true /* beginTouch */, false /* endTouch */, units.gu(10), 50);
360+
361+ compare(eventEaterArea.enabled, true);
362+
363+ touchRelease(phoneStage, endX, endY);
364+
365+ compare(eventEaterArea.enabled, false);
366+ }
367+
349 function test_leftEdge_data() {
368 function test_leftEdge_data() {
350 return [
369 return [
351 { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(5), shouldMoveApp: true },
370 { tag: "normal", inSpread: false, leftEdgeDragWidth: units.gu(5), shouldMoveApp: true },
tests/qmltests/tst_Shell.qml+14−17
@@ -83,7 +83,7 @@ Rectangle {
83 Shell {
83 Shell {
84 property string shellMode: "full-greeter" /* default */
84 property string shellMode: "full-greeter" /* default */
85
85
86− usageScenario: "phone"
86+ usageScenario: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
87 orientation: Qt.PortraitOrientation
87 orientation: Qt.PortraitOrientation
88 primaryOrientation: Qt.PortraitOrientation
88 primaryOrientation: Qt.PortraitOrientation
89 nativeOrientation: Qt.PortraitOrientation
89 nativeOrientation: Qt.PortraitOrientation
@@ -151,14 +151,11 @@ Rectangle {
151 }
151 }
152 }
152 }
153 ListItem.ItemSelector {
153 ListItem.ItemSelector {
154− id: usageModeSelector
154+ id: usageScenarioSelector
155 anchors { left: parent.left; right: parent.right }
155 anchors { left: parent.left; right: parent.right }
156 activeFocusOnPress: false
156 activeFocusOnPress: false
157− text: "Usage mode"
157+ text: "Usage scenario"
158− model: ["Staged", "Windowed"]
158+ model: ["phone", "tablet", "desktop"]
159− onSelectedIndexChanged: {
160− GSettingsController.setUsageMode(model[selectedIndex]);
161− }
162 }
159 }
163 MouseTouchEmulationCheckbox {
160 MouseTouchEmulationCheckbox {
164 id: mouseEmulation
161 id: mouseEmulation
@@ -725,7 +722,7 @@ Rectangle {
725
722
726 tryCompare(webApp.session.surface, "activeFocus", true);
723 tryCompare(webApp.session.surface, "activeFocus", true);
727
724
728− GSettingsController.setUsageMode("Windowed");
725+ shell.usageScenario = "desktop";
729
726
730 // check that the desktop stage and window have been loaded
727 // check that the desktop stage and window have been loaded
731 {
728 {
@@ -735,7 +732,7 @@ Rectangle {
735
732
736 tryCompare(webApp.session.surface, "activeFocus", true);
733 tryCompare(webApp.session.surface, "activeFocus", true);
737
734
738− GSettingsController.setUsageMode("Staged");
735+ shell.usageScenario = "tablet";
739
736
740 // check that the tablet stage and app surface delegate have been loaded
737 // check that the tablet stage and app surface delegate have been loaded
741 {
738 {
@@ -1205,30 +1202,30 @@ Rectangle {
1205
1202
1206 function test_stageLoader_data() {
1203 function test_stageLoader_data() {
1207 return [
1204 return [
1208− {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageMode: "Staged"},
1205+ {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageScenario: "phone"},
1209− {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageMode: "Staged"},
1206+ {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageScenario: "tablet"},
1210− {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageMode: "Windowed"}
1207+ {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageScenario: "desktop"}
1211 ]
1208 ]
1212 }
1209 }
1213
1210
1214 function test_stageLoader(data) {
1211 function test_stageLoader(data) {
1215− GSettingsController.setUsageMode(data.usageMode);
1216 loadShell(data.formFactor);
1212 loadShell(data.formFactor);
1213+ shell.usageScenario = data.usageScenario;
1217 var stageLoader = findChild(shell, "applicationsDisplayLoader");
1214 var stageLoader = findChild(shell, "applicationsDisplayLoader");
1218 verify(String(stageLoader.source).indexOf(data.source) >= 0);
1215 verify(String(stageLoader.source).indexOf(data.source) >= 0);
1219 }
1216 }
1220
1217
1221 function test_launcherInverted_data() {
1218 function test_launcherInverted_data() {
1222 return [
1219 return [
1223− {tag: "phone", formFactor: "phone", usageMode: "Staged", launcherInverted: true},
1220+ {tag: "phone", formFactor: "phone", usageScenario: "phone", launcherInverted: true},
1224− {tag: "tablet", formFactor: "tablet", usageMode: "Staged", launcherInverted: true},
1221+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet", launcherInverted: true},
1225− {tag: "desktop", formFactor: "tablet", usageMode: "Windowed", launcherInverted: false}
1222+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop", launcherInverted: false}
1226 ]
1223 ]
1227 }
1224 }
1228
1225
1229 function test_launcherInverted(data) {
1226 function test_launcherInverted(data) {
1230− GSettingsController.setUsageMode(data.usageMode);
1231 loadShell(data.formFactor);
1227 loadShell(data.formFactor);
1228+ shell.usageScenario = data.usageScenario;
1232
1229
1233 var launcher = findChild(shell, "launcher");
1230 var launcher = findChild(shell, "launcher");
1234 compare(launcher.inverted, data.launcherInverted);
1231 compare(launcher.inverted, data.launcherInverted);