on xenial help sign in
Merge trunk.
commitedd1e52c0b1444bb525282ca7899ffe78683a7b5 authorMichał Sawicz <michal.sawicz@canonical.com> dateTue 27 May 2014, 22:48 (12 years ago) parent44bdd4e8478dba
32 files changed+500−303
cmake/modules/QmlTest.cmake+4−4 debian/changelog+41 plugins/Dash/CardCreator.js+3−3 plugins/Dash/abstractdashview.cpp+3 po/unity8.pot+11−7 qml/Components/PinPadButton.qml+2−1 qml/Dash/CardCarousel.qml+1−1 qml/Dash/GenericScopeView.qml+1−1 qml/Dash/Previews/PreviewHeader.qml+3 qml/Greeter/Lockscreen.qml+91−7 qml/Greeter/PassphraseLockscreen.qml+6−2 qml/Greeter/PinLockscreen.qml+29−11 qml/Shell.qml+6−11 tests/autopilot/unity8/shell/emulators/main_window.py−4 tests/mocks/Unity/CMakeLists.txt−1 tests/mocks/Unity/fake_categories.cpp+5−1 tests/mocks/Unity/iconutils.cpp−124 tests/mocks/Unity/iconutils.h−29 tests/plugins/Dash/cardcreator/1.res+3−3 tests/plugins/Dash/cardcreator/2.res+1−1 tests/plugins/Dash/cardcreator/3.res+3−3 tests/plugins/Dash/cardcreator/4.res+1−1 tests/plugins/Dash/cardcreator/5.res+3−3 tests/plugins/Dash/organicgridtest.cpp+5 tests/qmltests/CMakeLists.txt−1 tests/qmltests/Components/tst_ZoomableImage.qml+21−19 tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+12−7 tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−5 tests/qmltests/Dash/tst_DashContent.qml+29−14 tests/qmltests/Dash/tst_GenericScopeView.qml+1 tests/qmltests/Greeter/tst_Lockscreen.qml+208−30 tests/qmltests/tst_Shell.qml−9
cmake/modules/QmlTest.cmake+4−4
@@ -51,7 +51,7 @@ macro(add_manual_qml_test SUBPATH COMPONENT_NAME)
5151
5252 set(qmlscene_command
5353 env ${qmltest_ENVIRONMENT}
54− ${qmlscene_exe} ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
54+ ${qmlscene_exe} -qmljsdebugger=port:3768 ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
5555 ${qmlscene_imports}
5656 )
5757 add_custom_target(${qmlscene_TARGET} ${qmlscene_command})
@@ -111,9 +111,9 @@ macro(add_qml_test_internal SUBPATH COMPONENT_NAME ITERATIONS)
111111 -o -,txt
112112 ${function_ARGS}
113113 )
114− find_program( HAVE_GCC gcc )
115− if (NOT ${HAVE_GCC} STREQUAL "")
116− exec_program( gcc ARGS "-dumpmachine" OUTPUT_VARIABLE ARCH_TRIPLET )
114+ find_program(DPKG dpkg-architecture)
115+ if(DPKG)
116+ exec_program(${DPKG} ARGS "-qDEB_BUILD_MULTIARCH" OUTPUT_VARIABLE ARCH_TRIPLET )
117117 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
118118 endif()
119119 set(qmltest_xvfb_command
debian/changelog+41
@@ -1,3 +1,44 @@
1+unity8 (7.86+14.10.20140527-0ubuntu1) utopic; urgency=low
2+
3+ [ Andrea Cimitan ]
4+ * Passes to make tryCommand -qmljsdebugger=port:3768 to enable
5+ debug/profiling test apps
6+ * Fixes carousel shadow
7+
8+ [ Michał Sawicz ]
9+ * Use dpkg-architecture, not gcc, to determine the machine triplet.
10+
11+ [ Ying-Chun Liu ]
12+ * Fix ZoomableImage test failure. (LP: #1317254)
13+
14+ [ Albert Astals ]
15+ * We don't need iconutils in this mock
16+ * Don't reserve space for mascot if no mascot is specified (LP:
17+ #1319343)
18+ * CardHeader is no more, remove stale line in CMakeLists.txt
19+ * GenericScopeViewTest: Wait a bit more Otherwise sometimes we end
20+ getting up the wrong delegate (maybe one that will be garbage
21+ collected?) (LP: #1322279)
22+ * Fix crash in organicgridtest
23+
24+ [ Daniel d'Andrada ]
25+ * Remove Shell's underlay background image as it cannot be seen
26+ anymore Now that the Dash has its own, opaque, background, the
27+ underlay's background image can no longer be seen. So it's just a
28+ waste of resources to have it.
29+
30+ [ Michael Terry ]
31+ * Use the same animation when dismissing a greeter slide from the
32+ launcher as from a normal greeter drag. (LP: #1316513)
33+
34+ [ Michael Zanetti ]
35+ * enhance lockscreen add a retry indication label (e.g. 3 attempts
36+ left). add an additional label (e.g. phone number for multi sim).
37+ add a infoPopup (e.g. to display a warning for last retry). add min
38+ and max limit values. add tests for the above (LP: #1302050)
39+
40+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 27 May 2014 07:47:11 +0000
41+
142unity8 (7.86+14.10.20140522-0ubuntu1) utopic; urgency=low
243
344 [ Albert Astals ]
plugins/Dash/CardCreator.js+3−3
@@ -70,10 +70,9 @@ var kArtShapeHolderCode = 'Item { \n\
7070 radius: "medium"; \n\
7171 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
7272 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
73− Component.onCompleted: updateWidthHeightBindings(); \n\
73+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; } \n\
7474 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
7575 visible: image.status == Image.Ready; \n\
76− borderSource: artShapeBorderSource; \n\
7776 function updateWidthHeightBindings() { \n\
7877 if (aspectSmallerThanImageAspect) { \n\
7978 width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
@@ -281,7 +280,7 @@ function cardString(template, components) {
281280 property var template; \n\
282281 property var components; \n\
283282 property var cardData; \n\
284− property var artShapeBorderSource; \n\
283+ property var artShapeBorderSource: undefined; \n\
285284 property real fontScale: 1.0; \n\
286285 property int headerAlignment: Text.AlignLeft; \n\
287286 property int fixedHeaderHeight: -1; \n\
@@ -307,6 +306,7 @@ function cardString(template, components) {
307306 }
308307
309308 if (hasArt) {
309+ code += 'onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; } \n';
310310 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
311311
312312 var widthCode, heightCode;
plugins/Dash/abstractdashview.cpp+3
@@ -176,6 +176,9 @@ void AbstractDashView::refill()
176176
177177bool AbstractDashView::addVisibleItems(qreal fillFromY, qreal fillToY, bool asynchronous)
178178{
179+ if (fillToY <= fillFromY)
180+ return false;
181+
179182 if (!delegate())
180183 return false;
181184
po/unity8.pot+11−7
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2014-05-27 14:52+0200\n"
11+"POT-Creation-Date: 2014-05-27 22:48+0200\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -117,24 +117,28 @@ msgstr ""
117117msgid "Swipe to unlock"
118118msgstr ""
119119
120−#: qml/Greeter/Lockscreen.qml:149
120+#: qml/Greeter/Lockscreen.qml:215
121121msgid "Emergency Call"
122122msgstr ""
123123
124−#: qml/Greeter/PassphraseLockscreen.qml:59
124+#: qml/Greeter/Lockscreen.qml:232
125+msgid "OK"
126+msgstr ""
127+
128+#: qml/Greeter/PassphraseLockscreen.qml:60
125129#, qt-format
126130msgid "Hello %1"
127131msgstr ""
128132
129−#: qml/Greeter/PassphraseLockscreen.qml:59
133+#: qml/Greeter/PassphraseLockscreen.qml:60
130134msgid "Hello"
131135msgstr ""
132136
133−#: qml/Greeter/PinLockscreen.qml:180
137+#: qml/Greeter/PinLockscreen.qml:198
134138msgid "CANCEL"
135139msgstr ""
136140
137−#: qml/Greeter/PinLockscreen.qml:198
141+#: qml/Greeter/PinLockscreen.qml:216
138142msgid "DONE"
139143msgstr ""
140144
@@ -170,7 +174,7 @@ msgstr ""
170174msgid "Search"
171175msgstr ""
172176
173−#: qml/Shell.qml:342
177+#: qml/Shell.qml:334
174178#, qt-format
175179msgid "Please enter %1"
176180msgstr ""
qml/Components/PinPadButton.qml+2−1
@@ -19,6 +19,7 @@ import Ubuntu.Components 0.1
1919
2020Item {
2121 id: root
22+ opacity: enabled ? 1 : 0.6
2223
2324 property alias text: label.text
2425 property alias subText: subTextLabel.text
@@ -62,7 +63,7 @@ Item {
6263 Label {
6364 id: subTextLabel
6465 fontSize: "small"
65− color: "grey"
66+ color: "#f3f3e7"
6667 anchors.horizontalCenter: parent.horizontalCenter
6768 visible: text.length > 0
6869 }
qml/Dash/CardCarousel.qml+1−1
@@ -34,7 +34,7 @@ DashRenderer {
3434 Carousel {
3535 id: carousel
3636 anchors.fill: parent
37− tileAspectRatio: cardCarousel.components && cardCarousel.components["art"]["aspect-ratio"] || 1.0
37+ tileAspectRatio: cardTool.components && cardTool.components["art"]["aspect-ratio"] || 1.0
3838 // FIXME we need to "reverse" the carousel to make the selected item the size
3939 // and push others back.
4040 minimumTileWidth: cardTool.cardWidth / selectedItemScaleFactor
qml/Dash/GenericScopeView.qml+1−1
@@ -128,7 +128,7 @@ FocusScope {
128128
129129 CardTool {
130130 id: cardTool
131−
131+ objectName: "cardTool"
132132 count: results.count
133133 template: model.renderer
134134 components: model.components
qml/Dash/Previews/PreviewHeader.qml+3
@@ -56,6 +56,9 @@ PreviewWidget {
5656
5757 Loader {
5858 id: mascotShapeLoader
59+ objectName: "mascotShapeLoader"
60+ active: headerRoot.mascot != ""
61+ visible: active
5962
6063 anchors.verticalCenter: parent.verticalCenter
6164 // TODO karni: Icon aspect-ratio is 8:7.5. Revisit these values to avoid fraction of pixels.
qml/Greeter/Lockscreen.qml+91−7
@@ -15,7 +15,8 @@
1515 */
1616
1717import QtQuick 2.0
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.0
19+import Ubuntu.Components.Popups 1.0
1920import "../Components"
2021
2122Showable {
@@ -26,19 +27,34 @@ Showable {
2627
2728 // Placeholder text
2829 property string placeholderText: ""
30+ // Placeholder text while shaking (e.g. Incorrect passprase)
31+ property string wrongPlaceholderText: ""
32+ // Retries text (e.g. 3 retries left)
33+ property string retryText: ""
34+
35+ // Informational text. (e.g. some text to tell which domain this is pin is entered for.
36+ property string infoText: ""
2937
3038 // In case the Lockscreen can show a greeter message, this is the username
3139 property string username: ""
3240
33− // Set this to a value greater 0 to enable auto-confirm behavior for the lockscreen.
34− // This is ignored by the alphaNumeric lockscreen as that one is confirmed with pressing enter on the OSK.
35− property int pinLength: -1
41+ // Set those to a value greater 0 to restrict the pin length.
42+ // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
43+ // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
44+ // entered pin is at least that long. If maxPinLength is set, the lockscreen won't allow entering any
45+ // more numbers than that. If both are set to the same value, the lockscreen will enter auto confirming
46+ // behavior, hiding the confirmation button and triggering that automatically when the entered pin reached
47+ // that length. This is ignored by the alphaNumeric lockscreen as that one is always confirmed by pressing
48+ // enter on the OSK.
49+ property int minPinLength: -1
50+ property int maxPinLength: -1
3651
3752 property url background: ""
3853
3954 signal entered(string passphrase)
4055 signal cancel()
4156 signal emergencyCall()
57+ signal infoPopupConfirmed()
4258
4359 onRequiredChanged: {
4460 if (required && pinPadLoader.item) {
@@ -56,6 +72,10 @@ Showable {
5672 pinPadLoader.item.clear(showAnimation);
5773 }
5874
75+ function showInfoPopup(title, text) {
76+ PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
77+ }
78+
5979 Rectangle {
6080 // In case background fails to load or is undefined
6181 id: backgroundBackup
@@ -68,7 +88,6 @@ Showable {
6888 objectName: "lockscreenBackground"
6989 anchors {
7090 fill: parent
71− topMargin: backgroundTopMargin
7291 }
7392 source: root.required ? root.background : ""
7493 fillMode: Image.PreserveAspectCrop
@@ -78,6 +97,43 @@ Showable {
7897 anchors.fill: root
7998 }
8099
100+ Column {
101+ spacing: units.gu(2)
102+ anchors {
103+ left: parent.left
104+ right: parent.right
105+ bottom: pinPadLoader.top
106+ bottomMargin: units.gu(2)
107+ }
108+ Label {
109+ objectName: "retryCountLabel"
110+ anchors {
111+ left: parent.left
112+ right: parent.right
113+ }
114+ text: root.retryText
115+ horizontalAlignment: Text.AlignHCenter
116+ color: "#f3f3e7"
117+ opacity: 0.6
118+ visible: root.retryText.length > 0
119+ }
120+ Label {
121+ id: infoTextLabel
122+ objectName: "infoTextLabel"
123+ anchors {
124+ left: parent.left
125+ right: parent.right
126+ }
127+ text: root.infoText
128+ horizontalAlignment: Text.AlignHCenter
129+ color: "#f3f3e7"
130+ opacity: 0.6
131+ visible: root.infoText.length > 0
132+ }
133+ }
134+
135+
136+
81137 Loader {
82138 id: pinPadLoader
83139 objectName: "pinPadLoader"
@@ -105,14 +161,24 @@ Showable {
105161
106162 Binding {
107163 target: pinPadLoader.item
108− property: "pinLength"
109− value: root.pinLength
164+ property: "minPinLength"
165+ value: root.minPinLength
166+ }
167+ Binding {
168+ target: pinPadLoader.item
169+ property: "maxPinLength"
170+ value: root.maxPinLength
110171 }
111172 Binding {
112173 target: pinPadLoader.item
113174 property: "placeholderText"
114175 value: root.placeholderText
115176 }
177+ Binding {
178+ target: pinPadLoader.item
179+ property: "wrongPlaceholderText"
180+ value: root.wrongPlaceholderText
181+ }
116182 Binding {
117183 target: pinPadLoader.item
118184 property: "username"
@@ -153,4 +219,22 @@ Showable {
153219 anchors.horizontalCenter: parent.horizontalCenter
154220 }
155221 }
222+
223+ Component {
224+ id: infoPopupComponent
225+ Dialog {
226+ id: dialog
227+ objectName: "infoPopup"
228+ modal: true
229+
230+ Button {
231+ objectName: "infoPopupOkButton"
232+ text: i18n.tr("OK")
233+ onClicked: {
234+ PopupUtils.close(dialog)
235+ root.infoPopupConfirmed();
236+ }
237+ }
238+ }
239+ }
156240}
qml/Greeter/PassphraseLockscreen.qml+6−2
@@ -22,7 +22,8 @@ Item {
2222 id: root
2323 height: highlightItem.height
2424
25− property alias placeholderText: pinentryField.placeholderText
25+ property string placeholderText
26+ property string wrongPlaceholderText
2627 property string username: ""
2728
2829 signal entered(string passphrase)
@@ -73,9 +74,12 @@ Item {
7374 echoMode: TextInput.Password
7475 opacity: 0.9
7576 hasClearButton: false
77+ placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
7678
7779 onAccepted: {
78− root.entered(pinentryField.text);
80+ if (pinentryField.text) {
81+ root.entered(pinentryField.text);
82+ }
7983 }
8084 }
8185 }
qml/Greeter/PinLockscreen.qml+29−11
@@ -25,9 +25,11 @@ Column {
2525 spacing: units.gu(3.5)
2626
2727 property alias placeholderText: pinentryField.placeholderText
28+ property alias wrongPlaceholderText: pinentryField.wrongPlaceholderText
2829 property int padWidth: units.gu(34)
2930 property int padHeight: units.gu(28)
30− property int pinLength: -1
31+ property int minPinLength: -1
32+ property int maxPinLength: -1
3133
3234 signal entered(string passphrase)
3335 signal cancel()
@@ -41,6 +43,10 @@ Column {
4143 }
4244 }
4345
46+ QtObject {
47+ id: priv
48+ property bool autoConfirm: root.minPinLength == root.maxPinLength && root.minPinLength != -1
49+ }
4450
4551 UbuntuShape {
4652 id: pinentryField
@@ -52,12 +58,20 @@ Column {
5258 radius: "medium"
5359 property string text: ""
5460 property string placeholderText: ""
61+ property string wrongPlaceholderText: ""
62+
63+ function appendChar(character) {
64+ if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
65+ pinentryField.text = pinentryField.text + character;
66+ }
67+ }
68+
5569 onTextChanged: {
5670 pinentryFieldLabel.text = "";
5771 for (var i = 0; i < text.length; ++i) {
5872 pinentryFieldLabel.text += "•";
5973 }
60− if (text.length === root.pinLength) {
74+ if (priv.autoConfirm && text.length === root.maxPinLength) {
6175 root.entered(text);
6276 }
6377 }
@@ -74,9 +88,11 @@ Column {
7488 }
7589 Label {
7690 id: pinentryFieldPlaceHolder
91+ objectName: "pinentryFieldPlaceHolder"
7792 anchors.centerIn: parent
78− color: "grey"
79− text: parent.placeholderText
93+ color: "#f3f3e7"
94+ opacity: 0.6
95+ text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
8096 visible: pinentryFieldLabel.text.length == 0
8197 }
8298
@@ -91,9 +107,11 @@ Column {
91107 bottom: parent.bottom
92108 bottomMargin: units.gu(1)
93109 }
94− visible: root.pinLength == -1
110+ visible: !priv.autoConfirm
95111 width: height
96112 name: "erase"
113+ color: "#f3f3e7"
114+ opacity: 0.6
97115 MouseArea {
98116 anchors.fill: parent
99117 onClicked: pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
@@ -168,7 +186,7 @@ Column {
168186 enabled: entryEnabled
169187
170188 onClicked: {
171− pinentryField.text = pinentryField.text + text;
189+ pinentryField.appendChar(text);
172190 }
173191 }
174192 }
@@ -186,7 +204,7 @@ Column {
186204 width: root.padWidth / 3
187205 height: root.padHeight / 4
188206 text: "0"
189− onClicked: pinentryField.text = pinentryField.text + text
207+ onClicked: pinentryField.appendChar(text);
190208 enabled: entryEnabled
191209 }
192210
@@ -194,16 +212,16 @@ Column {
194212 objectName: "pinPadButtonErase"
195213 width: root.padWidth / 3
196214 height: root.padHeight / 4
197− iconName: root.pinLength == -1 ? "" : "erase"
198− subText: root.pinLength == -1 ? i18n.tr("DONE") : ""
215+ iconName: priv.autoConfirm ? "erase" : ""
216+ subText: priv.autoConfirm ? "" : i18n.tr("DONE")
199217 onClicked: {
200− if (root.pinLength !== -1) {
218+ if (priv.autoConfirm) {
201219 pinentryField.text = pinentryField.text.substring(0, pinentryField.text.length-1);
202220 } else {
203221 root.entered(pinentryField.text);
204222 }
205223 }
206− enabled: entryEnabled
224+ enabled: priv.autoConfirm ? entryEnabled : pinentryField.text.length >= root.minPinLength
207225 }
208226 }
209227 }
qml/Shell.qml+6−11
@@ -117,15 +117,6 @@ FocusScope {
117117 // through the translucent parts of the shell surface.
118118 visible: !fullyCovered && !applicationSurfaceShouldBeSeen
119119
120− CrossFadeImage {
121− id: backgroundImage
122− objectName: "backgroundImage"
123−
124− anchors.fill: parent
125− source: shell.background
126− fillMode: Image.PreserveAspectCrop
127− }
128−
129120 Rectangle {
130121 anchors.fill: parent
131122 color: "black"
@@ -316,7 +307,8 @@ FocusScope {
316307 width: parent.width
317308 height: parent.height - panel.panelHeight
318309 background: shell.background
319− pinLength: 4
310+ minPinLength: 4
311+ maxPinLength: 4
320312
321313 onEntered: LightDM.Greeter.respond(passphrase);
322314 onCancel: greeter.show()
@@ -370,7 +362,10 @@ FocusScope {
370362 width: parent.width
371363 height: parent.height - panel.panelHeight
372364
373− Behavior on x {SmoothedAnimation{velocity: 600}}
365+ Behavior on x {
366+ enabled: !launcher.dashSwipe
367+ StandardAnimation {}
368+ }
374369
375370 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
376371
tests/autopilot/unity8/shell/emulators/main_window.py−4
@@ -114,10 +114,6 @@ class QQuickView(emulators.UnityEmulatorBase):
114114 self.wait_select_single('Indicators', fullyOpened=True)
115115 return self._get_indicator_page(indicator_name)
116116
117− def get_shell_background(self):
118− return self.select_single(
119− "CrossFadeImage", objectName="backgroundImage")
120−
121117 @autopilot_logging.log_action(logger.info)
122118 def show_dash_swiping(self):
123119 """Show the dash swiping from the left."""
tests/mocks/Unity/CMakeLists.txt−1
@@ -19,7 +19,6 @@ include_directories(
1919add_definitions(-DQT_NO_KEYWORDS)
2020
2121set(UnityQML_SOURCES
22− iconutils.cpp
2322 fake_scope.cpp
2423 fake_scopes.cpp
2524 fake_categories.cpp
tests/mocks/Unity/fake_categories.cpp+5−1
@@ -152,7 +152,11 @@ Categories::data(const QModelIndex& index, int role) const
152152 case RoleComponents:
153153 {
154154 QVariantMap map, artMap;
155− artMap["aspect-ratio"] = "1.0";
155+ if (index.row() % 2 != 0) {
156+ artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
157+ } else {
158+ artMap["aspect-ratio"] = "1.0";
159+ }
156160 artMap["field"] = "art";
157161 map["art"] = artMap;
158162 map["title"] = "HOLA";
tests/mocks/Unity/iconutils.cppdeleted−124
@@ -1,124 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Michal Hruby <michal.hruby@canonical.com>
18− */
19−
20−#include "iconutils.h"
21−
22−#include <gio/gio.h>
23−
24−#include <QStringList>
25−#include <QUrl>
26−#include <QUrlQuery>
27−
28−#define BASE_THEME_ICON_URI "image://theme/"
29−#define BASE_THUMBNAILER_URI "image://thumbnailer/"
30−#define BASE_ALBUMART_URI "image://albumart/"
31−
32−QString gIconToDeclarativeImageProviderString(QString const &giconString)
33−{
34− if (giconString.isEmpty()) return giconString;
35−
36− if (giconString.startsWith('/') || giconString.startsWith(QLatin1String("http")) ||
37− giconString.startsWith(QLatin1String("file:")) || giconString.startsWith(QLatin1String("image:"))) {
38− return giconString;
39− }
40−
41− if (!giconString.startsWith(QLatin1String(". "))) {
42− // must be a themed icon
43− QString themedIcon(BASE_THEME_ICON_URI);
44− themedIcon.append(giconString);
45− return themedIcon;
46− }
47−
48− // special case annotated icon
49− if (giconString.startsWith(QLatin1String(". UnityProtocolAnnotatedIcon "))) {
50− QString annotatedIcon;
51− QString serializedData(QUrl::fromPercentEncoding(giconString.mid(29).toUtf8()));
52− GVariant *variant = g_variant_parse(G_VARIANT_TYPE_VARDICT, serializedData.toUtf8().constData(), NULL, NULL, NULL);
53− gchar *baseUri;
54− if (variant != NULL && g_variant_lookup(variant, "base-icon", "&s", &baseUri)) {
55− annotatedIcon = gIconToDeclarativeImageProviderString(QString(baseUri));
56− // FIXME: enclose in image://annotated/... once unity supports that
57− }
58− if (variant != NULL) g_variant_unref(variant);
59−
60− return annotatedIcon;
61− }
62−
63− // handle real gicon strings
64− QString result;
65− GError *error = NULL;
66− GIcon *icon = g_icon_new_for_string(giconString.toLocal8Bit().constData(), &error);
67− if (error != NULL || icon == NULL) {
68− qWarning("Unable to deserialize icon: %s", giconString.toLocal8Bit().constData());
69− g_clear_error(&error);
70− return result;
71− }
72−
73− if (G_IS_THEMED_ICON(icon)) {
74− QString themedIcon(BASE_THEME_ICON_URI);
75− QStringList list;
76− const char* const *iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon));
77− if (iconNames != NULL) {
78− for (const char * const *iter = iconNames; *iter != NULL; ++iter) {
79− list << QLatin1String(*iter);
80− }
81− }
82− themedIcon.append(list.join(QString(",")));
83− result = themedIcon;
84− } else if (G_IS_FILE_ICON(icon)) {
85− GFile *file = g_file_icon_get_file(G_FILE_ICON(icon));
86− gchar *uri = g_file_get_uri(file);
87− QString iconUri(uri);
88− g_free(uri);
89− result = iconUri;
90− }
91−
92− g_object_unref(icon);
93−
94− return result;
95−}
96−
97−QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype, QVariantHash const &metadata)
98−{
99− if (uri.startsWith(QLatin1String("file:///"))) {
100− bool isAudio = mimetype.startsWith(QLatin1String("audio/"));
101− QString thumbnailerUri;
102− if (isAudio) {
103− thumbnailerUri = BASE_ALBUMART_URI;
104− if (metadata.contains("content")) {
105− QVariantHash contentHash = metadata["content"].toHash();
106− if (contentHash.contains("content")) { // nested content in Home?
107− contentHash = contentHash["content"].toHash();
108− }
109− if (contentHash.contains("album") && contentHash.contains("artist")) {
110− QUrlQuery query;
111− query.addQueryItem(QStringLiteral("artist"), contentHash["artist"].toString());
112− query.addQueryItem(QStringLiteral("album"), contentHash["album"].toString());
113− thumbnailerUri.append(query.toString());
114− }
115− }
116− } else {
117− thumbnailerUri = BASE_THUMBNAILER_URI;
118− thumbnailerUri.append(uri.midRef(7));
119− }
120− return thumbnailerUri;
121− }
122−
123− return QString::null;
124−}
tests/mocks/Unity/iconutils.hdeleted−29
@@ -1,29 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Michal Hruby <michal.hruby@canonical.com>
18− */
19−
20−#ifndef ICONUTILS_H
21−#define ICONUTILS_H
22−
23−#include <QString>
24−#include <QVariant>
25−
26−QString gIconToDeclarativeImageProviderString(QString const &giconString);
27−QString uriToThumbnailerProviderString(QString const &uri, QString const &mimetype, QVariantHash const &metadata);
28−
29−#endif
tests/plugins/Dash/cardcreator/1.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/2.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/3.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/cardcreator/4.res+1−1
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
tests/plugins/Dash/cardcreator/5.res+3−3
@@ -3,7 +3,7 @@ AbstractButton {
33 property var template;
44 property var components;
55 property var cardData;
6− property var artShapeBorderSource;
6+ property var artShapeBorderSource: undefined;
77 property real fontScale: 1.0;
88 property int headerAlignment: Text.AlignLeft;
99 property int fixedHeaderHeight: -1;
@@ -12,6 +12,7 @@ AbstractButton {
1212 property bool asynchronous: true;
1313 property bool showHeader: true;
1414 implicitWidth: childrenRect.width;
15+onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
1516readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
1617Item {
1718 id: artShapeHolder;
@@ -30,10 +31,9 @@ Item {
3031 radius: "medium";
3132 readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
3233 readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33− Component.onCompleted: updateWidthHeightBindings();
34+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
3435 onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
3536 visible: image.status == Image.Ready;
36− borderSource: artShapeBorderSource;
3737 function updateWidthHeightBindings() {
3838 if (aspectSmallerThanImageAspect) {
3939 width = Qt.binding(function() { return !visible ? 0 : image.width });
tests/plugins/Dash/organicgridtest.cpp+5
@@ -514,6 +514,11 @@ private Q_SLOTS:
514514
515515 model->setCount(21);
516516
517+ QTRY_COMPARE(grid->m_visibleItems.count(), 0);
518+
519+ grid->setDisplayMarginBeginning(0);
520+ grid->setDisplayMarginEnd(0);
521+
517522 // This is exactly the same block as the first again
518523 checkInitialPositions();
519524 }
tests/qmltests/CMakeLists.txt−1
@@ -36,7 +36,6 @@ add_qml_test(Dash Dash IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAUL
3636add_qml_test(Dash DashContent IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
3737add_qml_test(Dash Card IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
3838add_qml_benchmark(Dash CardBenchmark 1000 IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
39−add_qml_test(Dash CardHeader)
4039add_qml_test(Dash CardTool IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4140add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
4241add_qml_test(Dash/Apps RunningApplicationsGrid IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS}
tests/qmltests/Components/tst_ZoomableImage.qml+21−19
@@ -98,22 +98,23 @@ Rectangle {
9898 // move mouse to center
9999 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
100100
101+ var oldScale = image.scale;
101102 // Test Zoom-in Zoom-out twice.
102103 for (var c=0; c<2; c++) {
103104 // zoom in
104105 for (var i=0; i<10; i++) {
105106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106− tryCompare(image, "scale", 1.0 + (i + 1) * 0.1);
107− compare(flickable.contentWidth, lazyImage.width * image.scale);
108− compare(flickable.contentHeight, lazyImage.height * image.scale);
107+ tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
108+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
109+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
109110 }
110111
111112 // zoom out
112113 for (var i=0; i<10; i++) {
113114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114− tryCompare(image, "scale", 2.0 - (i + 1) * 0.1);
115− compare(flickable.contentWidth, lazyImage.width * image.scale);
116− compare(flickable.contentHeight, lazyImage.height * image.scale);
115+ tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
116+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
117+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
117118 }
118119 }
119120 }
@@ -123,16 +124,12 @@ Rectangle {
123124 zoomable:false,
124125 answer1: true,
125126 answer2: false,
126− answer3: true,
127− answer4: 1.0,
128− answer5: 1.0 },
127+ answer3: true },
129128 { source:widgetData2["source"],
130129 zoomable:true,
131130 answer1: false,
132131 answer2: true,
133− answer3: false,
134− answer4: 1.7740461882048026,
135− answer5: 1.0 }
132+ answer3: false }
136133 ]
137134 }
138135
@@ -163,7 +160,7 @@ Rectangle {
163160 var y2End = zoomableImage.height * 5 / 6;
164161
165162 var oldScale = image.scale;
166−
163+ var newScale = -1;
167164 // move mouse to center
168165 mouseMove(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2);
169166
@@ -172,20 +169,25 @@ Rectangle {
172169 wait(3000); // have to delay between two consequent pinch event.
173170 // pinch zoom-in
174171 touchPinch(zoomableImage, x1Start, y1Start, x1End, y1End, x2Start, y2Start, x2End, y2End);
172+ waitForRendering(image);
175173
176− tryCompare(image, "scale", data.answer4);
177− var newScale = image.scale;
174+ if (newScale == -1) {
175+ newScale = image.scale;
176+ }
177+ tryCompare(image, "scale", newScale);
178178 compare(newScale == oldScale, data.answer1, "scale factor not equal: "+ oldScale + "=?" + newScale);
179179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
180180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
181− compare(newScale, data.answer4, "scale factor error");
182− compare(flickable.contentWidth, lazyImage.width * image.scale);
183− compare(flickable.contentHeight, lazyImage.height * image.scale);
181+ compare(image.scale, newScale, "scale factor error");
182+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
183+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
184184
185185 wait(3000); // have to delay between two consequent pinch event.
186186 // pinch zoom-out
187187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
188− tryCompare(image, "scale", data.answer5);
188+ tryCompare(image, "scale", oldScale);
189+ compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
190+ compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
189191 }
190192 }
191193 }
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+12−7
@@ -53,12 +53,12 @@ Rectangle {
5353 name: "PreviewHeaderTest"
5454 when: windowShown
5555
56− property Item mascot: findChild(previewHeader, "mascotShape")
56+ property Item mascotShapeLoader: findChild(previewHeader, "mascotShapeLoader")
5757 property Item outerRow: findChild(previewHeader, "outerRow")
5858 property Item column: findChild(previewHeader, "column")
5959
6060 function initTestCase() {
61− verify(typeof testCase.mascot === "object", "Couldn't find mascot object.");
61+ verify(typeof testCase.mascotShapeLoader === "object", "Couldn't find mascot loader object.");
6262 verify(typeof testCase.outerRow === "object", "Couldn't find outerRow object.");
6363 verify(typeof testCase.column === "object", "Couldn't find column object.");
6464
@@ -70,9 +70,9 @@ Rectangle {
7070
7171 function test_mascot_data() {
7272 return [
73− { tag: "Empty", source: "", visible: false },
74− { tag: "Invalid", source: "bad_path", visible: false },
75− { tag: "Valid", source: "../graphics/play_button.png", visible: true },
73+ { tag: "Empty", source: "", loaderVisible: false, visible: false },
74+ { tag: "Invalid", source: "bad_path", loaderVisible: true, visible: false },
75+ { tag: "Valid", source: "../graphics/play_button.png", loaderVisible: true, visible: true },
7676 ]
7777 }
7878
@@ -80,12 +80,17 @@ Rectangle {
8080 headerjson.mascot = data.source;
8181 previewHeader.widgetData = headerjson;
8282
83− tryCompare(testCase.mascot, "visible", data.visible);
83+ tryCompare(testCase.mascotShapeLoader, "visible", data.loaderVisible);
84+ if (data.loaderVisible) {
85+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
86+ var mascot = findChild(previewHeader, "mascotShape")
87+ tryCompare(mascot, "visible", data.visible);
88+ }
8489 }
8590
8691 function test_dimensions_data() {
8792 return [
88− { tag: "Column width with mascot", object: column, width: previewHeader.width - mascot.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
93+ { tag: "Column width with mascot", object: column, width: previewHeader.width - mascotShapeLoader.width - outerRow.margins * 3, mascot: "artwork/avatar.png" },
8994 { tag: "Header height", object: previewHeader, height: function() { return outerRow.height + outerRow.margins * 2 } },
9095 ]
9196 }
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−5
@@ -26,12 +26,12 @@ Rectangle {
2626 color: "lightgrey"
2727
2828 property var widgetData0: {
29− "source": ""
29+ "source": Qt.resolvedUrl("../artwork/checkers.png"),
30+ "zoomable": false
3031 }
3132
3233 property var widgetData1: {
33− "source": Qt.resolvedUrl("../artwork/checkers.png"),
34− "zoomable": false
34+ "source": ""
3535 }
3636
3737 PreviewZoomableImage {
@@ -49,11 +49,13 @@ Rectangle {
4949
5050 zoomableImage.widgetData = widgetData0;
5151 waitForRendering(zoomableImage);
52− tryCompare(image, "imageState", "default");
52+ waitForRendering(image);
53+ tryCompare(image, "imageState", "ready");
5354
5455 zoomableImage.widgetData = widgetData1;
5556 waitForRendering(zoomableImage);
56− tryCompare(image, "imageState", "ready");
57+ waitForRendering(image);
58+ tryCompare(image, "imageState", "default");
5759 }
5860 }
5961}
tests/qmltests/Dash/tst_DashContent.qml+29−14
@@ -67,13 +67,17 @@ Item {
6767 name: "DashContent"
6868 when: scopesModel.loaded && windowShown
6969
70− function init() {
71− scopesModel.clear();
70+ function loadScopes() {
7271 scopeLoadedSpy.clear();
7372 scopesModel.load();
7473 tryCompare(scopeLoadedSpy, "count", 4);
7574 }
7675
76+ function init() {
77+ scopesModel.clear();
78+ loadScopes();
79+ }
80+
7781 function cleanup() {
7882 movementStartedSpy.clear();
7983 dashContent.visible = true;
@@ -93,9 +97,7 @@ Item {
9397 compare(dashContentList.count, 0, "DashContent should have 0 items when it starts");
9498 tryCompare(dashContentList, "currentIndex", -1);
9599
96− scopeLoadedSpy.clear();
97− scopesModel.load();
98− tryCompare(scopeLoadedSpy, "count", 4);
100+ loadScopes();
99101
100102 verify(dashContentList.currentIndex >= 0);
101103 }
@@ -109,9 +111,7 @@ Item {
109111 // pretend we're running after a model reset
110112 dashContentList.currentIndex = 27;
111113
112− scopeLoadedSpy.clear();
113− scopesModel.load();
114− tryCompare(scopeLoadedSpy, "count", 4);
114+ loadScopes();
115115
116116 verify(dashContentList.currentIndex >= 0 && dashContentList.currentIndex < 5);
117117 }
@@ -326,8 +326,6 @@ Item {
326326 }
327327
328328 function test_previewOpenClose() {
329− tryCompare(scopeLoadedSpy, "count", 4);
330−
331329 var previewListView = findChild(dashContent, "dashContentPreviewList");
332330 tryCompare(previewListView, "open", false);
333331
@@ -352,8 +350,6 @@ Item {
352350 },
353351 true);
354352
355− tryCompare(scopeLoadedSpy, "count", 4);
356−
357353 var previewListView = findChild(dashContent, "dashContentPreviewList");
358354 tryCompare(previewListView, "open", false);
359355
@@ -368,8 +364,6 @@ Item {
368364 }
369365
370366 function test_previewCycle() {
371− tryCompare(scopeLoadedSpy, "count", 4);
372−
373367 var categoryListView = findChild(dashContent, "categoryListView");
374368 categoryListView.positionAtBeginning();
375369
@@ -392,5 +386,26 @@ Item {
392386 }
393387 closePreview();
394388 }
389+
390+ function test_carouselAspectRatio() {
391+ tryCompareFunction(function() {
392+ var scope = findChild(dashContent, "MockScope1 loader");
393+ if (scope != null) {
394+ var dashCategory1 = findChild(scope, "dashCategory1");
395+ if (dashCategory1 != null) {
396+ var tile = findChild(dashCategory1, "carouselDelegate1");
397+ return tile != null;
398+ }
399+ }
400+ return false;
401+ },
402+ true);
403+
404+ var scope = findChild(dashContent, "MockScope1 loader");
405+ var dashCategory1 = findChild(scope, "dashCategory1");
406+ var cardTool = findChild(dashCategory1, "cardTool");
407+ var carouselLV = findChild(dashCategory1, "listView");
408+ verify(carouselLV.tileWidth / carouselLV.tileHeight == cardTool.components["art"]["aspect-ratio"]);
409+ }
395410 }
396411}
tests/qmltests/Dash/tst_GenericScopeView.qml+1
@@ -126,6 +126,7 @@ Item {
126126
127127 function test_filter_expand_collapse() {
128128 // wait for the item to be there
129+ waitForRendering(genericScopeView);
129130 tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
130131
131132 var header = findChild(genericScopeView, "dashSectionHeader0")
tests/qmltests/Greeter/tst_Lockscreen.qml+208−30
@@ -23,25 +23,34 @@ import LightDM 0.1 as LightDM
2323import Unity.Test 0.1 as UT
2424
2525Rectangle {
26− width: units.gu(60)
26+ id: root
27+ width: units.gu(80)
2728 height: units.gu(80)
2829 color: "orange"
2930
3031 Lockscreen {
3132 id: lockscreen
3233 anchors.fill: parent
33− anchors.rightMargin: units.gu(18)
34+ anchors.rightMargin: units.gu(30)
3435 placeholderText: "Please enter your PIN"
36+ wrongPlaceholderText: "Incorrect PIN"
37+ retryText: retryCountTextField.text
3538 alphaNumeric: pinPadCheckBox.checked
36− pinLength: pinLengthTextField.text
39+ minPinLength: minPinLengthTextField.text
40+ maxPinLength: maxPinLengthTextField.text
3741 username: "Lola"
42+ background: "../../../qml/graphics/phone_background.jpg"
43+ infoText: infoTextTextField.text
3844 }
3945
4046 Connections {
4147 target: lockscreen
4248
4349 onEmergencyCall: emergencyCheckBox.checked = true
44− onEntered: enteredLabel.text = passphrase
50+ onEntered: {
51+ enteredLabel.text = passphrase
52+ lockscreen.clear(true)
53+ }
4554 }
4655
4756 Connections {
@@ -49,9 +58,9 @@ Rectangle {
4958
5059 onShowPrompt: {
5160 if (text.indexOf("PIN") >= 0) {
52− lockscreen.alphaNumeric = false
61+ pinPadCheckBox.checked = false
5362 } else {
54− lockscreen.alphaNumeric = true
63+ pinPadCheckBox.checked = true
5564 }
5665 lockscreen.placeholderText = text;
5766 }
@@ -65,6 +74,7 @@ Rectangle {
6574 Column {
6675 anchors.fill: parent
6776 anchors.margins: units.gu(1)
77+ spacing: units.gu(1)
6878
6979 Row {
7080 CheckBox {
@@ -86,12 +96,32 @@ Rectangle {
8696 }
8797 Row {
8898 TextField {
89− id: pinLengthTextField
99+ id: minPinLengthTextField
90100 width: units.gu(7)
91101 text: "4"
92102 }
93103 Label {
94− text: "PIN length"
104+ text: "Min PIN length"
105+ }
106+ }
107+ Row {
108+ TextField {
109+ id: maxPinLengthTextField
110+ width: units.gu(7)
111+ text: "4"
112+ }
113+ Label {
114+ text: "Max PIN length"
115+ }
116+ }
117+ Row {
118+ TextField {
119+ id: retryCountTextField
120+ width: units.gu(7)
121+ text: "3 retries left"
122+ }
123+ Label {
124+ text: "Retries left"
95125 }
96126 }
97127 Label {
@@ -108,12 +138,40 @@ Rectangle {
108138 }
109139 Button {
110140 text: "start auth (1234)"
141+ width: parent.width
111142 onClicked: LightDM.Greeter.authenticate("has-pin")
112143 }
113144 Button {
114145 text: "start auth (password)"
146+ width: parent.width
115147 onClicked: LightDM.Greeter.authenticate("has-password")
116148 }
149+
150+ TextField {
151+ id: infoTextTextField
152+ width: parent.width
153+ placeholderText: "Infotext"
154+ text: "+49 179 3253553"
155+ }
156+
157+ TextField {
158+ id: infoPopupTitleTextField
159+ width: parent.width
160+ placeholderText: "Popup title"
161+ text: "This will be the last attempt"
162+ }
163+
164+ TextArea {
165+ id: infoPopupTextArea
166+ width: parent.width
167+ text: "If the SIM PIN is entered incorrectly, your SIM will be blocked and would require the PUK code to unlock."
168+ }
169+
170+ Button {
171+ text: "open info popup"
172+ width: parent.width
173+ onClicked: lockscreen.showInfoPopup(infoPopupTitleTextField.text, infoPopupTextArea.text)
174+ }
117175 }
118176 }
119177
@@ -121,6 +179,16 @@ Rectangle {
121179 name: "Lockscreen"
122180 when: windowShown
123181
182+ function cleanup() {
183+ lockscreen.clear(false);
184+ }
185+
186+ function waitForLockscreenReady() {
187+ var pinPadLoader = findChild(lockscreen, "pinPadLoader");
188+ tryCompare(pinPadLoader, "status", Loader.Ready)
189+ waitForRendering(lockscreen)
190+ }
191+
124192 function test_loading_data() {
125193 return [
126194 {tag: "numeric", alphanumeric: false, pinPadAvailable: true },
@@ -129,8 +197,8 @@ Rectangle {
129197 }
130198
131199 function test_loading(data) {
132− lockscreen.alphaNumeric = data.alphanumeric
133− waitForRendering(lockscreen)
200+ pinPadCheckBox.checked = data.alphanumeric
201+ waitForLockscreenReady();
134202 if (data.pinPadAvailable) {
135203 compare(findChild(lockscreen, "pinPadButton8").text, "8", "Could not find number 8 on PinPad")
136204 } else {
@@ -147,8 +215,8 @@ Rectangle {
147215
148216 function test_emergency_call(data) {
149217 emergencyCheckBox.checked = false
150− lockscreen.alphaNumeric = data.alphanumeric
151− waitForRendering(lockscreen)
218+ pinPadCheckBox.checked = data.alphanumeric
219+ waitForLockscreenReady();
152220 var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
153221 mouseClick(emergencyButton, units.gu(1), units.gu(1))
154222 tryCompare(emergencyCheckBox, "checked", true)
@@ -163,9 +231,9 @@ Rectangle {
163231 }
164232
165233 function test_labels(data) {
166− lockscreen.alphaNumeric = data.alphanumeric
234+ pinPadCheckBox.checked = data.alphanumeric
167235 lockscreen.placeholderText = data.placeholderText
168− waitForRendering(lockscreen)
236+ waitForLockscreenReady();
169237 compare(findChild(lockscreen, "pinentryField").placeholderText, data.placeholderText, "Placeholdertext is not what it should be")
170238 if (data.alphanumeric) {
171239 compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
@@ -175,23 +243,25 @@ Rectangle {
175243
176244 function test_unlock_data() {
177245 return [
178− {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", pinLength: 4},
179− {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", pinLength: -1},
180− {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", pinLength: 4},
181− {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", pinLength: -1},
182− {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", pinLength: -1},
246+ {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
247+ {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", minPinLength: -1, maxPinLength: -1},
248+ {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", minPinLength: 4, maxPinLength: 4},
249+ {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", minPinLength: -1, maxPinLength: -1},
250+ {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: -1, maxPinLength: -1},
183251 ]
184252 }
185253
186254 function test_unlock(data) {
187255 enteredLabel.text = ""
188− pinLengthTextField.text = data.pinLength
256+ minPinLengthTextField.text = data.minPinLength
257+ maxPinLengthTextField.text = data.maxPinLength
189258 LightDM.Greeter.authenticate(data.username)
190− waitForRendering(lockscreen)
259+ waitForLockscreenReady();
191260
192261 var inputField = findChild(lockscreen, "pinentryField")
193262 if (data.alphanumeric) {
194263 mouseClick(inputField, units.gu(1), units.gu(1))
264+ tryCompare(inputField, "focus", true);
195265 typeString(data.password)
196266 keyClick(Qt.Key_Enter)
197267 } else {
@@ -200,7 +270,7 @@ Rectangle {
200270 var button = findChild(lockscreen, "pinPadButton" + character)
201271 mouseClick(button, units.gu(1), units.gu(1))
202272 }
203− if (data.pinLength == -1) {
273+ if (data.minPinLength !== data.maxPinLength || data.minPinLength == -1) {
204274 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
205275 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
206276 }
@@ -219,11 +289,12 @@ Rectangle {
219289
220290 function test_clear(data) {
221291 pinPadCheckBox.checked = data.alphanumeric
222− waitForRendering(lockscreen)
292+ waitForLockscreenReady();
223293
224294 var inputField = findChild(lockscreen, "pinentryField")
225295 if (data.alphanumeric) {
226296 mouseClick(inputField, units.gu(1), units.gu(1))
297+ tryCompare(inputField, "activeFocus", true);
227298 typeString("1")
228299 } else {
229300 var button = findChild(lockscreen, "pinPadButton1")
@@ -239,6 +310,16 @@ Rectangle {
239310
240311 wait(0) // Trigger event loop to make sure the animation would start running
241312 compare(animation.running, data.animation)
313+ if (data.animation) {
314+ if (data.alphanumeric) {
315+ tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
316+ tryCompare(inputField, "placeholderText", lockscreen.placeholderText)
317+ } else {
318+ var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
319+ tryCompare(label, "text", lockscreen.wrongPlaceholderText)
320+ tryCompare(label, "text", lockscreen.placeholderText)
321+ }
322+ }
242323
243324 // wait for animation to finish to not disturb other tests
244325 tryCompare(animation, "running", false)
@@ -246,33 +327,130 @@ Rectangle {
246327
247328 function test_backspace_data() {
248329 return [
249− {tag: "fixed length", pinLength: 4},
250− {tag: "variable length", pinLength: -1}
330+ {tag: "fixed length", minPinLength: 4, maxPinLength: 4},
331+ {tag: "variable undefined length", minPinLength: -1, maxPinLength: -1},
332+ {tag: "variable restricted length", minPinLength: 4, maxPinLength: 8}
251333 ];
252334 }
253335
254336 function test_backspace(data) {
255337 pinPadCheckBox.checked = false
256− pinLengthTextField.text = data.pinLength
257− waitForRendering(lockscreen);
338+ minPinLengthTextField.text = data.minPinLength
339+ maxPinLengthTextField.text = data.maxPinLength
340+ waitForLockscreenReady();
258341
259342 var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
260343 var backspaceIcon = findChild(lockscreen, "backspaceIcon");
261344 var pinEntryField = findChild(lockscreen, "pinentryField");
262345
263− compare(pinPadButtonErase.iconName, data.pinLength == -1 ? "" : "erase");
264− compare(backspaceIcon.visible, data.pinLength == -1);
346+ var autoConfirmEnabled = data.minPinLength === data.maxPinLength && data.minPinLength !== -1;
347+ compare(pinPadButtonErase.iconName, autoConfirmEnabled ? "erase" : "");
348+ compare(backspaceIcon.visible, !autoConfirmEnabled);
265349
266350 var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
267351 mouseClick(pinPadButton5, units.gu(1), units.gu(1));
268352 compare(pinEntryField.text, "5");
269353
270− if (data.pinLength == -1) {
354+ if (data.minPinLength !== data.maxPinLength) {
271355 mouseClick(backspaceIcon, units.gu(1), units.gu(1));
272356 } else {
273357 mouseClick(pinPadButtonErase, units.gu(1), units.gu(1));
274358 }
275359 compare(pinEntryField.text, "");
276360 }
361+
362+ function test_minMaxLength_data() {
363+ return [
364+ {tag: "undefined", minPinLength: -1, maxPinLength: -1},
365+ {tag: "fixed", minPinLength: 4, maxPinLength: 4},
366+ {tag: "variable, limited", minPinLength: 4, maxPinLength: 8},
367+ ];
368+ }
369+
370+ function test_minMaxLength(data) {
371+ pinPadCheckBox.checked = false
372+ minPinLengthTextField.text = data.minPinLength
373+ maxPinLengthTextField.text = data.maxPinLength
374+ waitForLockscreenReady();
375+
376+ var pinPadButton5 = findChild(lockscreen, "pinPadButton5");
377+ var pinPadButtonErase = findChild(lockscreen, "pinPadButtonErase");
378+ var inputField = findChild(lockscreen, "pinentryField")
379+
380+ for (var i = 0; i < 10; i++) {
381+ mouseClick(pinPadButton5, units.gu(1), units.gu(1));
382+
383+ if (data.minPinLength == data.maxPinLength && data.minPinLength != -1) {
384+ // auto confirm mode...
385+ compare(inputField.text.length, (i+1) % data.minPinLength);
386+ } else {
387+ // manual confirm mode
388+ tryCompare(pinPadButtonErase, "enabled", (i+1) >= data.minPinLength)
389+ if (data.maxPinLength == -1) {
390+ compare(inputField.text.length, i+1);
391+ } else {
392+ compare(inputField.text.length, Math.min(data.maxPinLength, i+1));
393+ }
394+
395+ }
396+ }
397+ }
398+
399+ function test_retryDisplay_data() {
400+ return [
401+ {tag: "empty", retryText: "", shown: false},
402+ {tag: "3 retries left", retryText: "3 retries left", shown: true},
403+ ]
404+ }
405+
406+ function test_retryDisplay(data) {
407+ pinPadCheckBox.checked = false
408+ waitForLockscreenReady();
409+
410+ retryCountTextField.text = data.retryText;
411+ var label = findChild(lockscreen, "retryCountLabel")
412+ compare(label.visible, data.shown);
413+ }
414+
415+ function test_infoPopup() {
416+ verify(findChild(root, "infoPopup") === null);
417+ lockscreen.showInfoPopup("foo", "bar");
418+ tryCompareFunction(function() { return findChild(root, "infoPopup") !== null}, true);
419+
420+ var infoPopup = findChild(root, "infoPopup");
421+ compare(infoPopup.title, "foo");
422+ compare(infoPopup.text, "bar");
423+
424+ signalSpy.signalName = "infoPopupConfirmed"
425+ signalSpy.clear();
426+
427+ var okButton = findChild(root, "infoPopupOkButton");
428+ mouseClick(okButton, okButton.width / 2, okButton.height / 2);
429+
430+ tryCompareFunction(function() { return findChild(root, "infoPopup") === null}, true);
431+
432+ tryCompare(signalSpy, "count", 1);
433+ }
434+
435+ function test_infoTextDisplay_data() {
436+ return [
437+ {tag: "empty string", text: "", shown: false},
438+ {tag: "hello world", text: "hello world", shown: true},
439+ ]
440+ }
441+
442+ function test_infoTextDisplay(data) {
443+ pinPadCheckBox.checked = false
444+ waitForLockscreenReady();
445+
446+ infoTextTextField.text = data.text;
447+ var label = findChild(lockscreen, "infoTextLabel")
448+ compare(label.visible, data.shown);
449+ }
450+ }
451+
452+ SignalSpy {
453+ id: signalSpy
454+ target: lockscreen
277455 }
278456}
tests/qmltests/tst_Shell.qml−9
@@ -475,15 +475,6 @@ Item {
475475 } while (!isStill);
476476 }
477477
478− function test_background_data() {
479− return [
480− {tag: "red", url: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), expectedUrl: "tests/data/unity/backgrounds/red.png"},
481− {tag: "blue", url: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), expectedUrl: "tests/data/unity/backgrounds/blue.png"},
482− {tag: "invalid", url: "invalid", expectedUrl: shell.defaultBackground},
483− {tag: "empty", url: "", expectedUrl: shell.defaultBackground}
484− ]
485− }
486−
487478 function test_DashShown_data() {
488479 return [
489480 {tag: "in focus", greeter: false, app: false, launcher: false, indicators: false, expectedShown: true},