Merge lp:~unity-team/unity8/ota9.5 into lp:unity8/stable
- ota9.5
- Merge into stable
Proposed by
Michał Sawicz
Status: | Merged |
---|---|
Approved by: | Michał Sawicz |
Approved revision: | 2142 |
Merged at revision: | 2140 |
Proposed branch: | lp:~unity-team/unity8/ota9.5 |
Merge into: | lp:unity8/stable |
Diff against target: |
755 lines (+464/-66) 13 files modified
plugins/Dash/CardCreator.js (+34/-30) plugins/Dash/CardCreatorCache.qml (+1/-1) plugins/Dash/listviewwithpageheader.cpp (+18/-9) plugins/Dash/listviewwithpageheader.h (+1/-0) tests/plugins/Dash/cardcreator/10.res (+142/-0) tests/plugins/Dash/cardcreator/10.tst (+2/-1) tests/plugins/Dash/cardcreator/11.res (+217/-0) tests/plugins/Dash/cardcreator/11.tst (+3/-0) tests/plugins/Dash/cardcreator/3.res (+1/-1) tests/plugins/Dash/cardcreator/6.res (+1/-1) tests/plugins/Dash/cardcreatortest.cpp (+21/-22) tests/plugins/Dash/listviewwithpageheadertest.cpp (+23/-0) tests/plugins/Dash/listviewwithpageheadertest.qml (+0/-1) |
To merge this branch: | bzr merge lp:~unity-team/unity8/ota9.5 |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michał Sawicz | code | Approve | |
Review via email:
|
Commit message
[r2175] LVWPH: Reset to initial values when list is empty
[r2166] Better attempt at sanitization
[r2162] Do not use the same filepath parameter for all the card creator createQmlObject calls
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'plugins/Dash/CardCreator.js' |
2 | --- plugins/Dash/CardCreator.js 2016-01-21 22:33:46 +0000 |
3 | +++ plugins/Dash/CardCreator.js 2016-02-11 14:27:32 +0000 |
4 | @@ -16,9 +16,9 @@ |
5 | |
6 | .pragma library |
7 | |
8 | -// %1 is the template["card-background"] string |
9 | -// %2 is the template["card-background"]["elements"][0] |
10 | -// %3 is the template["card-background"]["elements"][1] |
11 | +// %1 is the template["card-background"]["elements"][0] |
12 | +// %2 is the template["card-background"]["elements"][1] |
13 | +// %3 is the template["card-background"] string |
14 | var kBackgroundLoaderCode = 'Loader {\n\ |
15 | id: backgroundLoader; \n\ |
16 | objectName: "backgroundLoader"; \n\ |
17 | @@ -46,14 +46,14 @@ |
18 | objectName: "backgroundImage"; \n\ |
19 | source: { \n\ |
20 | if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\ |
21 | - else return "%1"; \n\ |
22 | + else return %3; \n\ |
23 | } \n\ |
24 | } \n\ |
25 | function getColor(index) { \n\ |
26 | if (cardData && typeof cardData["background"] === "object" \n\ |
27 | && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\ |
28 | return cardData["background"]["elements"][index]; \n\ |
29 | - } else return index === 0 ? %2 : %3; \n\ |
30 | + } else return index === 0 ? %1 : %2; \n\ |
31 | } \n\ |
32 | } \n\ |
33 | }\n'; |
34 | @@ -373,27 +373,21 @@ |
35 | color: %3; \n\ |
36 | }'; |
37 | |
38 | -function evil_param(object) { |
39 | - for (var x in object) { |
40 | - if (typeof object[x] === "object" && evil_param(object[x])) |
41 | - return true; |
42 | - |
43 | - if (typeof object[x] === "string" && object[x].match(/"(?:[^"\\]|\\.)*"/) != null) |
44 | - return true; |
45 | +function sanitizeColor(colorString) { |
46 | + if (colorString !== undefined) { |
47 | + if (colorString.match(/^[#a-z0-9]*$/i) === null) { |
48 | + // This is not the perfect regexp for color |
49 | + // but what we're trying to do here is just protect |
50 | + // against injection so it's ok |
51 | + return ""; |
52 | + } |
53 | } |
54 | - |
55 | - return false; |
56 | + return colorString; |
57 | } |
58 | |
59 | function cardString(template, components) { |
60 | var code; |
61 | |
62 | - if (evil_param(template)) |
63 | - return ""; |
64 | - |
65 | - if (evil_param(components)) |
66 | - return ""; |
67 | - |
68 | var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false"; |
69 | |
70 | code = 'AbstractButton { \n\ |
71 | @@ -442,18 +436,26 @@ |
72 | } |
73 | |
74 | if (hasBackground) { |
75 | - var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : ""; |
76 | + var templateCardBackground; |
77 | + if (template && typeof template["card-background"] === "string") { |
78 | + templateCardBackground = 'decodeURI("' + encodeURI(template["card-background"]) + '")'; |
79 | + } else { |
80 | + templateCardBackground = '""'; |
81 | + } |
82 | + |
83 | var backgroundElements0; |
84 | var backgroundElements1; |
85 | if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { |
86 | - if (template["card-background"]["elements"][0] !== undefined) { |
87 | - backgroundElements0 = '"%1"'.arg(template["card-background"]["elements"][0]); |
88 | + var element0 = sanitizeColor(template["card-background"]["elements"][0]); |
89 | + var element1 = sanitizeColor(template["card-background"]["elements"][1]); |
90 | + if (element0 !== undefined) { |
91 | + backgroundElements0 = '"%1"'.arg(element0); |
92 | } |
93 | - if (template["card-background"]["elements"][1] !== undefined) { |
94 | - backgroundElements1 = '"%1"'.arg(template["card-background"]["elements"][1]); |
95 | + if (element1 !== undefined) { |
96 | + backgroundElements1 = '"%1"'.arg(element1); |
97 | } |
98 | } |
99 | - code += kBackgroundLoaderCode.arg(templateCardBackground).arg(backgroundElements0).arg(backgroundElements1); |
100 | + code += kBackgroundLoaderCode.arg(backgroundElements0).arg(backgroundElements1).arg(templateCardBackground); |
101 | } |
102 | |
103 | if (hasArt) { |
104 | @@ -479,9 +481,10 @@ |
105 | } |
106 | |
107 | var fallback = components["art"] && components["art"]["fallback"] || ""; |
108 | + fallback = encodeURI(fallback); |
109 | var fallbackCode = ""; |
110 | if (fallback !== "") { |
111 | - fallbackCode += 'onStatusChanged: if (status === Image.Error) source = "%1";'.arg(fallback); |
112 | + fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback); |
113 | } |
114 | code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(fallbackCode); |
115 | } else { |
116 | @@ -576,9 +579,10 @@ |
117 | |
118 | var mascotImageVisible = useMascotShape ? 'false' : 'showHeader'; |
119 | var fallback = components["mascot"] && components["mascot"]["fallback"] || ""; |
120 | + fallback = encodeURI(fallback); |
121 | var fallbackCode = ""; |
122 | if (fallback !== "") { |
123 | - fallbackCode += 'onStatusChanged: if (status === Image.Error) source = "%1";'.arg(fallback); |
124 | + fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback); |
125 | } |
126 | mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackCode); |
127 | } |
128 | @@ -825,7 +829,7 @@ |
129 | return code; |
130 | } |
131 | |
132 | -function createCardComponent(parent, template, components) { |
133 | +function createCardComponent(parent, template, components, identifier) { |
134 | var imports = 'import QtQuick 2.4; \n\ |
135 | import Ubuntu.Components 1.3; \n\ |
136 | import Ubuntu.Settings.Components 0.1; \n\ |
137 | @@ -835,7 +839,7 @@ |
138 | var code = imports + 'Component {\n' + card + '}\n'; |
139 | |
140 | try { |
141 | - return Qt.createQmlObject(code, parent, "createCardComponent"); |
142 | + return Qt.createQmlObject(code, parent, identifier); |
143 | } catch (e) { |
144 | console.error("ERROR: Invalid component created."); |
145 | console.error("Template:"); |
146 | |
147 | === modified file 'plugins/Dash/CardCreatorCache.qml' |
148 | --- plugins/Dash/CardCreatorCache.qml 2015-07-15 15:07:19 +0000 |
149 | +++ plugins/Dash/CardCreatorCache.qml 2016-02-11 14:27:32 +0000 |
150 | @@ -32,7 +32,7 @@ |
151 | var allString = tString + cString; |
152 | var component = cache[allString]; |
153 | if (component === undefined) { |
154 | - component = CardCreator.createCardComponent(root, template, components); |
155 | + component = CardCreator.createCardComponent(root, template, components, allString); |
156 | cache[allString] = component; |
157 | } |
158 | return component; |
159 | |
160 | === modified file 'plugins/Dash/listviewwithpageheader.cpp' |
161 | --- plugins/Dash/listviewwithpageheader.cpp 2016-01-05 09:24:58 +0000 |
162 | +++ plugins/Dash/listviewwithpageheader.cpp 2016-02-11 14:27:32 +0000 |
163 | @@ -219,13 +219,7 @@ |
164 | Q_FOREACH(ListItem *item, m_visibleItems) |
165 | releaseItem(item); |
166 | m_visibleItems.clear(); |
167 | - m_firstVisibleIndex = -1; |
168 | - adjustMinYExtent(); |
169 | - setContentY(0); |
170 | - m_clipItem->setY(0); |
171 | - if (m_topSectionItem) { |
172 | - QQuickItemPrivate::get(m_topSectionItem)->setCulled(true); |
173 | - } |
174 | + initializeValuesForEmptyList(); |
175 | |
176 | m_delegateModel->setDelegate(delegate); |
177 | |
178 | @@ -236,6 +230,17 @@ |
179 | } |
180 | } |
181 | |
182 | +void ListViewWithPageHeader::initializeValuesForEmptyList() |
183 | +{ |
184 | + m_firstVisibleIndex = -1; |
185 | + adjustMinYExtent(); |
186 | + setContentY(0); |
187 | + m_clipItem->setY(0); |
188 | + if (m_topSectionItem) { |
189 | + QQuickItemPrivate::get(m_topSectionItem)->setCulled(true); |
190 | + } |
191 | +} |
192 | + |
193 | QQuickItem *ListViewWithPageHeader::header() const |
194 | { |
195 | return m_headerItem; |
196 | @@ -835,7 +840,7 @@ |
197 | } |
198 | } |
199 | if (!foundVisible) { |
200 | - m_firstVisibleIndex = -1; |
201 | + initializeValuesForEmptyList(); |
202 | } |
203 | if (m_firstVisibleIndex != oldFirstVisibleIndex) { |
204 | adjustMinYExtent(); |
205 | @@ -1092,7 +1097,11 @@ |
206 | } |
207 | |
208 | if (m_firstVisibleIndex != oldFirstVisibleIndex) { |
209 | - adjustMinYExtent(); |
210 | + if (m_visibleItems.isEmpty()) { |
211 | + initializeValuesForEmptyList(); |
212 | + } else { |
213 | + adjustMinYExtent(); |
214 | + } |
215 | } |
216 | |
217 | for (int i = 0; i < m_visibleItems.count(); ++i) { |
218 | |
219 | === modified file 'plugins/Dash/listviewwithpageheader.h' |
220 | --- plugins/Dash/listviewwithpageheader.h 2015-11-26 13:50:56 +0000 |
221 | +++ plugins/Dash/listviewwithpageheader.h 2016-02-11 14:27:32 +0000 |
222 | @@ -168,6 +168,7 @@ |
223 | QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted); |
224 | QQuickItem *getSectionItem(const QString §ionText); |
225 | void updateSectionItem(int modelIndex); |
226 | + void initializeValuesForEmptyList(); |
227 | |
228 | QQmlDelegateModel *m_delegateModel; |
229 | |
230 | |
231 | === added directory 'plugins/UInput' |
232 | === added file 'tests/plugins/Dash/cardcreator/10.res' |
233 | --- tests/plugins/Dash/cardcreator/10.res 1970-01-01 00:00:00 +0000 |
234 | +++ tests/plugins/Dash/cardcreator/10.res 2016-02-11 14:27:32 +0000 |
235 | @@ -0,0 +1,142 @@ |
236 | +AbstractButton { |
237 | + id: root; |
238 | + property var components; |
239 | + property var cardData; |
240 | + property string artShapeStyle: "inset"; |
241 | + property string backgroundShapeStyle: "inset"; |
242 | + property real fontScale: 1.0; |
243 | + property var scopeStyle: null; |
244 | + property int titleAlignment: Text.AlignLeft; |
245 | + property int fixedHeaderHeight: -1; |
246 | + property size fixedArtShapeSize: Qt.size(-1, -1); |
247 | + readonly property string title: cardData && cardData["title"] || ""; |
248 | + property bool asynchronous: true; |
249 | + property bool showHeader: true; |
250 | + implicitWidth: childrenRect.width; |
251 | + enabled: true; |
252 | + |
253 | +Loader { |
254 | + id: backgroundLoader; |
255 | + objectName: "backgroundLoader"; |
256 | + anchors.fill: parent; |
257 | + asynchronous: root.asynchronous; |
258 | + visible: status == Loader.Ready; |
259 | + sourceComponent: UbuntuShape { |
260 | + objectName: "background"; |
261 | + radius: "medium"; |
262 | + aspect: { |
263 | + switch (root.backgroundShapeStyle) { |
264 | + case "inset": return UbuntuShape.Inset; |
265 | + case "shadow": return UbuntuShape.DropShadow; |
266 | + default: |
267 | + case "flat": return UbuntuShape.Flat; |
268 | + } |
269 | + } |
270 | + backgroundColor: getColor(0) || "white"; |
271 | + secondaryBackgroundColor: getColor(1) || backgroundColor; |
272 | + backgroundMode: UbuntuShape.VerticalGradient; |
273 | + anchors.fill: parent; |
274 | + source: backgroundImage.source ? backgroundImage : null; |
275 | + property real luminance: Style.luminance(backgroundColor); |
276 | + property Image backgroundImage: Image { |
277 | + objectName: "backgroundImage"; |
278 | + source: { |
279 | + if (cardData && typeof cardData["background"] === "string") return cardData["background"]; |
280 | + else return ""; |
281 | + } |
282 | + } |
283 | + function getColor(index) { |
284 | + if (cardData && typeof cardData["background"] === "object" |
285 | + && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { |
286 | + return cardData["background"]["elements"][index]; |
287 | + } else return index === 0 ? "" : ""; |
288 | + } |
289 | + } |
290 | + } |
291 | +readonly property size artShapeSize: Qt.size(-1, -1); |
292 | +readonly property int headerHeight: row.height; |
293 | +Row { |
294 | + id: row; |
295 | + objectName: "outerRow"; |
296 | + property real margins: units.gu(1); |
297 | + spacing: margins; |
298 | + height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; |
299 | + anchors { top: parent.top; |
300 | + topMargin: units.gu(1); |
301 | +left: parent.left; |
302 | + } |
303 | + anchors.right: parent.right; |
304 | + anchors.margins: margins; |
305 | + anchors.rightMargin: 0; |
306 | + data: [ |
307 | + CroppedImageMinimumSourceSize { |
308 | + id: mascotImage; |
309 | + objectName: "mascotImage"; |
310 | + anchors { verticalCenter: parent.verticalCenter; } |
311 | + source: cardData && cardData["mascot"] || ""; |
312 | + width: units.gu(6); |
313 | + height: units.gu(5.625); |
314 | + horizontalAlignment: Image.AlignHCenter; |
315 | + verticalAlignment: Image.AlignVCenter; |
316 | + visible: showHeader; |
317 | + |
318 | + } |
319 | +,Item { |
320 | + id: headerTitleContainer; |
321 | + anchors { verticalCenter: parent.verticalCenter; } |
322 | + width: parent.width - x; |
323 | + implicitHeight: titleLabel.height + subtitleLabel.height; |
324 | + data: [ |
325 | + Label { |
326 | + id: titleLabel; |
327 | + objectName: "titleLabel"; |
328 | + anchors { right: parent.right; |
329 | +rightMargin: units.gu(1); |
330 | +left: parent.left; |
331 | + top: parent.top; } |
332 | + elide: Text.ElideRight; |
333 | + fontSize: "small"; |
334 | + wrapMode: Text.Wrap; |
335 | + maximumLineCount: 2; |
336 | + font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); |
337 | + color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white"); |
338 | + visible: showHeader ; |
339 | + width: undefined; |
340 | + text: root.title; |
341 | + font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal; |
342 | + horizontalAlignment: root.titleAlignment; |
343 | + } |
344 | +,Label { |
345 | + id: subtitleLabel; |
346 | + objectName: "subtitleLabel"; |
347 | + anchors { right: parent.right; |
348 | + left: parent.left; |
349 | +rightMargin: units.gu(1); |
350 | +top: titleLabel.bottom; |
351 | + } |
352 | + anchors.topMargin: units.dp(2); |
353 | + elide: Text.ElideRight; |
354 | + maximumLineCount: 1; |
355 | + fontSize: "x-small"; |
356 | + font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); |
357 | + color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white"); |
358 | + visible: titleLabel.visible && titleLabel.text; |
359 | + text: cardData && cardData["subtitle"] || ""; |
360 | + font.weight: Font.Light; |
361 | + } |
362 | + |
363 | + ] |
364 | + } |
365 | + |
366 | + ] |
367 | + } |
368 | +UbuntuShape { |
369 | + id: touchdown; |
370 | + objectName: "touchdown"; |
371 | + anchors { fill: backgroundLoader } |
372 | + visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed; |
373 | + radius: "medium"; |
374 | + borderSource: "radius_pressed.sci" |
375 | + } |
376 | +implicitHeight: row.y + row.height + units.gu(1); |
377 | +} |
378 | |
379 | === modified file 'tests/plugins/Dash/cardcreator/10.tst' |
380 | --- tests/plugins/Dash/cardcreator/10.tst 2016-01-21 22:33:46 +0000 |
381 | +++ tests/plugins/Dash/cardcreator/10.tst 2016-02-11 14:27:32 +0000 |
382 | @@ -1,2 +1,3 @@ |
383 | -template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2, "card-background": { "type": "color", "elements": [ "\" : \"\";} Item { } function moo() { return true ? \"" ] } } |
384 | +template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2, "card-background": { "type": "color", "elements": [ "\\", ": 3; } Item { } function moo () { \"" ] } } |
385 | components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}} |
386 | +result: 10.res |
387 | \ No newline at end of file |
388 | |
389 | === added file 'tests/plugins/Dash/cardcreator/11.res' |
390 | --- tests/plugins/Dash/cardcreator/11.res 1970-01-01 00:00:00 +0000 |
391 | +++ tests/plugins/Dash/cardcreator/11.res 2016-02-11 14:27:32 +0000 |
392 | @@ -0,0 +1,217 @@ |
393 | +AbstractButton { |
394 | + id: root; |
395 | + property var components; |
396 | + property var cardData; |
397 | + property string artShapeStyle: "inset"; |
398 | + property string backgroundShapeStyle: "inset"; |
399 | + property real fontScale: 1.0; |
400 | + property var scopeStyle: null; |
401 | + property int titleAlignment: Text.AlignLeft; |
402 | + property int fixedHeaderHeight: -1; |
403 | + property size fixedArtShapeSize: Qt.size(-1, -1); |
404 | + readonly property string title: cardData && cardData["title"] || ""; |
405 | + property bool asynchronous: true; |
406 | + property bool showHeader: true; |
407 | + implicitWidth: childrenRect.width; |
408 | + enabled: true; |
409 | + |
410 | +Loader { |
411 | + id: backgroundLoader; |
412 | + objectName: "backgroundLoader"; |
413 | + anchors.fill: parent; |
414 | + asynchronous: root.asynchronous; |
415 | + visible: status == Loader.Ready; |
416 | + sourceComponent: UbuntuShape { |
417 | + objectName: "background"; |
418 | + radius: "medium"; |
419 | + aspect: { |
420 | + switch (root.backgroundShapeStyle) { |
421 | + case "inset": return UbuntuShape.Inset; |
422 | + case "shadow": return UbuntuShape.DropShadow; |
423 | + default: |
424 | + case "flat": return UbuntuShape.Flat; |
425 | + } |
426 | + } |
427 | + backgroundColor: getColor(0) || "white"; |
428 | + secondaryBackgroundColor: getColor(1) || backgroundColor; |
429 | + backgroundMode: UbuntuShape.VerticalGradient; |
430 | + anchors.fill: parent; |
431 | + source: backgroundImage.source ? backgroundImage : null; |
432 | + property real luminance: Style.luminance(backgroundColor); |
433 | + property Image backgroundImage: Image { |
434 | + objectName: "backgroundImage"; |
435 | + source: { |
436 | + if (cardData && typeof cardData["background"] === "string") return cardData["background"]; |
437 | + else return ""; |
438 | + } |
439 | + } |
440 | + function getColor(index) { |
441 | + if (cardData && typeof cardData["background"] === "object" |
442 | + && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { |
443 | + return cardData["background"]["elements"][index]; |
444 | + } else return index === 0 ? "#E9E9E9" : undefined; |
445 | + } |
446 | + } |
447 | + } |
448 | +readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1); |
449 | +Item { |
450 | + id: artShapeHolder; |
451 | + height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height; |
452 | + width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width; |
453 | + anchors { horizontalCenter: parent.horizontalCenter; } |
454 | + Loader { |
455 | + id: artShapeLoader; |
456 | + objectName: "artShapeLoader"; |
457 | + active: cardData && cardData["art"] || false; |
458 | + asynchronous: root.asynchronous; |
459 | + visible: status == Loader.Ready; |
460 | + sourceComponent: Item { |
461 | + id: artShape; |
462 | + objectName: "artShape"; |
463 | + readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true; |
464 | + visible: image.status == Image.Ready; |
465 | + readonly property alias image: artImage; |
466 | + ShaderEffectSource { |
467 | + id: artShapeSource; |
468 | + sourceItem: artImage; |
469 | + anchors.centerIn: parent; |
470 | + width: 1; |
471 | + height: 1; |
472 | + hideSource: doShapeItem; |
473 | + } |
474 | + Loader { |
475 | + anchors.fill: parent; |
476 | + visible: artShape.doShapeItem; |
477 | + sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent; |
478 | + Component { |
479 | + id: artShapeShapeComponent; |
480 | + UbuntuShape { |
481 | + source: artShapeSource; |
482 | + sourceFillMode: UbuntuShape.PreserveAspectCrop; |
483 | + radius: "medium"; |
484 | + aspect: { |
485 | + switch (root.artShapeStyle) { |
486 | + case "inset": return UbuntuShape.Inset; |
487 | + case "shadow": return UbuntuShape.DropShadow; |
488 | + default: |
489 | + case "flat": return UbuntuShape.Flat; |
490 | + } |
491 | + } |
492 | + } |
493 | + } |
494 | + Component { |
495 | + id: artShapeIconComponent; |
496 | + ProportionalShape { source: artShapeSource; aspect: UbuntuShape.DropShadow; } |
497 | + } |
498 | + } |
499 | + readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; |
500 | + readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1; |
501 | + Component.onCompleted: { updateWidthHeightBindings(); } |
502 | + Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); } |
503 | + function updateWidthHeightBindings() { |
504 | + if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) { |
505 | + width = root.fixedArtShapeSize.width; |
506 | + height = root.fixedArtShapeSize.height; |
507 | + } else { |
508 | + width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width }); |
509 | + height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height }); |
510 | + } |
511 | + } |
512 | + CroppedImageMinimumSourceSize { |
513 | + id: artImage; |
514 | + objectName: "artImage"; |
515 | + source: cardData && cardData["art"] || ""; |
516 | + asynchronous: root.asynchronous; |
517 | + width: root.width; |
518 | + height: width / artShape.aspect; |
519 | + onStatusChanged: if (status === Image.Error) source = decodeURI("%5C"); |
520 | + } |
521 | + } |
522 | + } |
523 | + } |
524 | +readonly property int headerHeight: row.height; |
525 | +Row { |
526 | + id: row; |
527 | + objectName: "outerRow"; |
528 | + property real margins: units.gu(1); |
529 | + spacing: margins; |
530 | + height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; |
531 | + anchors { top: artShapeHolder.bottom; |
532 | + topMargin: units.gu(1); |
533 | +left: parent.left; |
534 | + } |
535 | + anchors.right: parent.right; |
536 | + anchors.margins: margins; |
537 | + anchors.rightMargin: 0; |
538 | + data: [ |
539 | + CroppedImageMinimumSourceSize { |
540 | + id: mascotImage; |
541 | + objectName: "mascotImage"; |
542 | + anchors { verticalCenter: parent.verticalCenter; } |
543 | + source: cardData && cardData["mascot"] || ""; |
544 | + width: units.gu(6); |
545 | + height: units.gu(5.625); |
546 | + horizontalAlignment: Image.AlignHCenter; |
547 | + verticalAlignment: Image.AlignVCenter; |
548 | + visible: showHeader; |
549 | + onStatusChanged: if (status === Image.Error) source = decodeURI("%22"); |
550 | + } |
551 | +,Item { |
552 | + id: headerTitleContainer; |
553 | + anchors { verticalCenter: parent.verticalCenter; } |
554 | + width: parent.width - x; |
555 | + implicitHeight: titleLabel.height + subtitleLabel.height; |
556 | + data: [ |
557 | + Label { |
558 | + id: titleLabel; |
559 | + objectName: "titleLabel"; |
560 | + anchors { right: parent.right; |
561 | +rightMargin: units.gu(1); |
562 | +left: parent.left; |
563 | + top: parent.top; } |
564 | + elide: Text.ElideRight; |
565 | + fontSize: "small"; |
566 | + wrapMode: Text.Wrap; |
567 | + maximumLineCount: 2; |
568 | + font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); |
569 | + color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white"); |
570 | + visible: showHeader ; |
571 | + width: undefined; |
572 | + text: root.title; |
573 | + font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal; |
574 | + horizontalAlignment: root.titleAlignment; |
575 | + } |
576 | +,Label { |
577 | + id: subtitleLabel; |
578 | + objectName: "subtitleLabel"; |
579 | + anchors { right: parent.right; |
580 | + left: parent.left; |
581 | +rightMargin: units.gu(1); |
582 | +top: titleLabel.bottom; |
583 | + } |
584 | + anchors.topMargin: units.dp(2); |
585 | + elide: Text.ElideRight; |
586 | + maximumLineCount: 1; |
587 | + fontSize: "x-small"; |
588 | + font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); |
589 | + color: backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white"); |
590 | + visible: titleLabel.visible && titleLabel.text; |
591 | + text: cardData && cardData["subtitle"] || ""; |
592 | + font.weight: Font.Light; |
593 | + } |
594 | + |
595 | + ] |
596 | + } |
597 | + |
598 | + ] |
599 | + } |
600 | +UbuntuShape { |
601 | + id: touchdown; |
602 | + objectName: "touchdown"; |
603 | + anchors { fill: backgroundLoader } |
604 | + visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed; |
605 | + radius: "medium"; |
606 | + borderSource: "radius_pressed.sci" |
607 | + } |
608 | +implicitHeight: row.y + row.height + units.gu(1); |
609 | +} |
610 | |
611 | === added file 'tests/plugins/Dash/cardcreator/11.tst' |
612 | --- tests/plugins/Dash/cardcreator/11.tst 1970-01-01 00:00:00 +0000 |
613 | +++ tests/plugins/Dash/cardcreator/11.tst 2016-02-11 14:27:32 +0000 |
614 | @@ -0,0 +1,3 @@ |
615 | +template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2 } |
616 | +components: {"art":{"aspect-ratio":1,"field":"art","fallback":"\\"},"background":{"field":"background"},"mascot":{"field":"icon","fallback":"\""},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}} |
617 | +result: 11.res |
618 | \ No newline at end of file |
619 | |
620 | === modified file 'tests/plugins/Dash/cardcreator/3.res' |
621 | --- tests/plugins/Dash/cardcreator/3.res 2016-01-21 22:44:41 +0000 |
622 | +++ tests/plugins/Dash/cardcreator/3.res 2016-02-11 14:27:32 +0000 |
623 | @@ -86,7 +86,7 @@ |
624 | asynchronous: root.asynchronous; |
625 | width: root.width; |
626 | height: width / artShape.aspect; |
627 | - onStatusChanged: if (status === Image.Error) source = "IHAVE\"ESCAPED\"QUOTES\""; |
628 | + onStatusChanged: if (status === Image.Error) source = decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22"); |
629 | } |
630 | } |
631 | } |
632 | |
633 | === modified file 'tests/plugins/Dash/cardcreator/6.res' |
634 | --- tests/plugins/Dash/cardcreator/6.res 2015-11-20 16:33:38 +0000 |
635 | +++ tests/plugins/Dash/cardcreator/6.res 2016-02-11 14:27:32 +0000 |
636 | @@ -42,7 +42,7 @@ |
637 | objectName: "backgroundImage"; |
638 | source: { |
639 | if (cardData && typeof cardData["background"] === "string") return cardData["background"]; |
640 | - else return "http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png"; |
641 | + else return decodeURI("http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png"); |
642 | } |
643 | } |
644 | function getColor(index) { |
645 | |
646 | === modified file 'tests/plugins/Dash/cardcreatortest.cpp' |
647 | --- tests/plugins/Dash/cardcreatortest.cpp 2016-01-21 22:33:46 +0000 |
648 | +++ tests/plugins/Dash/cardcreatortest.cpp 2016-02-11 14:27:32 +0000 |
649 | @@ -63,6 +63,7 @@ |
650 | |
651 | QVERIFY(lines[0].startsWith(templateString)); |
652 | QVERIFY(lines[1].startsWith(componentsString)); |
653 | + QVERIFY(lines[2].startsWith(resultString)); |
654 | |
655 | const QString templateJSON = lines[0].mid(templateString.length()); |
656 | const QString componentsJSON = lines[1].mid(componentsString.length()); |
657 | @@ -71,32 +72,30 @@ |
658 | QVariant cardStringResult; |
659 | QMetaObject::invokeMethod(view->rootObject(), "cardString", Q_RETURN_ARG(QVariant, cardStringResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON)); |
660 | |
661 | - if (!resultFileName.isEmpty()) { |
662 | - QFile testResultFile(testDirPath + resultFileName); |
663 | - QVERIFY(testResultFile.open(QIODevice::ReadOnly)); |
664 | - QTextStream ts2(&testResultFile); |
665 | - |
666 | - // Record failed results to /tmp |
667 | - const QString executedResult = cardStringResult.toString(); |
668 | - QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX"); |
669 | - tmpFile.open(); |
670 | - tmpFile.setAutoRemove(false); |
671 | - tmpFile.write(executedResult.toUtf8().constData()); |
672 | - |
673 | - // Line by line comparison |
674 | - const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n"); |
675 | - const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n"); |
676 | - for (int i = 0; i < expectedLines.size(); ++i) { |
677 | - QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified()); |
678 | - } |
679 | - |
680 | - // Remove the result if it passed |
681 | - tmpFile.setAutoRemove(true); |
682 | + QFile testResultFile(testDirPath + resultFileName); |
683 | + QVERIFY(testResultFile.open(QIODevice::ReadOnly)); |
684 | + QTextStream ts2(&testResultFile); |
685 | + |
686 | + // Record failed results to /tmp |
687 | + const QString executedResult = cardStringResult.toString(); |
688 | + QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX"); |
689 | + tmpFile.open(); |
690 | + tmpFile.setAutoRemove(false); |
691 | + tmpFile.write(executedResult.toUtf8().constData()); |
692 | + |
693 | + // Line by line comparison |
694 | + const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n"); |
695 | + const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n"); |
696 | + for (int i = 0; i < expectedLines.size(); ++i) { |
697 | + QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified()); |
698 | } |
699 | |
700 | + // Remove the result if it passed |
701 | + tmpFile.setAutoRemove(true); |
702 | + |
703 | QVariant createCardComponentResult; |
704 | QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON)); |
705 | - QCOMPARE(createCardComponentResult.toBool(), !resultFileName.isEmpty()); |
706 | + QVERIFY(createCardComponentResult.toBool()); |
707 | } |
708 | } |
709 | |
710 | |
711 | === modified file 'tests/plugins/Dash/listviewwithpageheadertest.cpp' |
712 | --- tests/plugins/Dash/listviewwithpageheadertest.cpp 2015-09-02 08:04:41 +0000 |
713 | +++ tests/plugins/Dash/listviewwithpageheadertest.cpp 2016-02-11 14:27:32 +0000 |
714 | @@ -1954,6 +1954,29 @@ |
715 | QCOMPARE(lvwph->m_firstVisibleIndex, 0); |
716 | } |
717 | |
718 | + void testBug1540490() |
719 | + { |
720 | + lvwph->header()->setImplicitHeight(150); |
721 | + verifyItem(0, 150., 150., false); |
722 | + |
723 | + QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 3), Q_ARG(QVariant, 3)); |
724 | + model->setProperty(0, "size", 400); |
725 | + model->setProperty(1, "size", 600); |
726 | + model->setProperty(2, "size", 300); |
727 | + |
728 | + scrollToBottom(); |
729 | + changeContentY(-200); |
730 | + |
731 | + QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 2)); |
732 | + model->setProperty(0, "size", 100); |
733 | + QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 1)); |
734 | + lvwph->header()->setImplicitHeight(50); |
735 | + QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 200)); |
736 | + |
737 | + QTRY_COMPARE(lvwph->m_visibleItems.count(), 1); |
738 | + verifyItem(0, 50., 200., false); |
739 | + } |
740 | + |
741 | private: |
742 | QQuickView *view; |
743 | ListViewWithPageHeader *lvwph; |
744 | |
745 | === modified file 'tests/plugins/Dash/listviewwithpageheadertest.qml' |
746 | --- tests/plugins/Dash/listviewwithpageheadertest.qml 2015-07-15 15:07:19 +0000 |
747 | +++ tests/plugins/Dash/listviewwithpageheadertest.qml 2016-02-11 14:27:32 +0000 |
748 | @@ -85,7 +85,6 @@ |
749 | pageHeader: Rectangle { |
750 | color: "transparent" |
751 | width: parent.width |
752 | - height: 50 |
753 | implicitHeight: 50 |
754 | Text { |
755 | anchors.fill: parent |
Verified the diff is sane for the cherry-picks.