Merge lp:~unity-team/unity8/ota9.5 into lp:unity8/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
Reviewer Review Type Date Requested Status
Michał Sawicz code Approve
Review via email: mp+285731@code.launchpad.net

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

To post a comment you must log in.
Revision history for this message
Michał Sawicz (saviq) wrote :

Verified the diff is sane for the cherry-picks.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'plugins/Dash/CardCreator.js'
--- plugins/Dash/CardCreator.js 2016-01-21 22:33:46 +0000
+++ plugins/Dash/CardCreator.js 2016-02-11 14:27:32 +0000
@@ -16,9 +16,9 @@
1616
17.pragma library17.pragma library
1818
19// %1 is the template["card-background"] string19// %1 is the template["card-background"]["elements"][0]
20// %2 is the template["card-background"]["elements"][0]20// %2 is the template["card-background"]["elements"][1]
21// %3 is the template["card-background"]["elements"][1]21// %3 is the template["card-background"] string
22var kBackgroundLoaderCode = 'Loader {\n\22var kBackgroundLoaderCode = 'Loader {\n\
23 id: backgroundLoader; \n\23 id: backgroundLoader; \n\
24 objectName: "backgroundLoader"; \n\24 objectName: "backgroundLoader"; \n\
@@ -46,14 +46,14 @@
46 objectName: "backgroundImage"; \n\46 objectName: "backgroundImage"; \n\
47 source: { \n\47 source: { \n\
48 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\48 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
49 else return "%1"; \n\49 else return %3; \n\
50 } \n\50 } \n\
51 } \n\51 } \n\
52 function getColor(index) { \n\52 function getColor(index) { \n\
53 if (cardData && typeof cardData["background"] === "object" \n\53 if (cardData && typeof cardData["background"] === "object" \n\
54 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\54 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
55 return cardData["background"]["elements"][index]; \n\55 return cardData["background"]["elements"][index]; \n\
56 } else return index === 0 ? %2 : %3; \n\56 } else return index === 0 ? %1 : %2; \n\
57 } \n\57 } \n\
58 } \n\58 } \n\
59 }\n';59 }\n';
@@ -373,27 +373,21 @@
373 color: %3; \n\373 color: %3; \n\
374 }';374 }';
375375
376function evil_param(object) {376function sanitizeColor(colorString) {
377 for (var x in object) {377 if (colorString !== undefined) {
378 if (typeof object[x] === "object" && evil_param(object[x]))378 if (colorString.match(/^[#a-z0-9]*$/i) === null) {
379 return true;379 // This is not the perfect regexp for color
380380 // but what we're trying to do here is just protect
381 if (typeof object[x] === "string" && object[x].match(/"(?:[^"\\]|\\.)*"/) != null)381 // against injection so it's ok
382 return true;382 return "";
383 }
383 }384 }
384385 return colorString;
385 return false;
386}386}
387387
388function cardString(template, components) {388function cardString(template, components) {
389 var code;389 var code;
390390
391 if (evil_param(template))
392 return "";
393
394 if (evil_param(components))
395 return "";
396
397 var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";391 var templateInteractive = (template == null ? true : (template["non-interactive"] !== undefined ? !template["non-interactive"] : true)) ? "true" : "false";
398392
399 code = 'AbstractButton { \n\393 code = 'AbstractButton { \n\
@@ -442,18 +436,26 @@
442 }436 }
443437
444 if (hasBackground) {438 if (hasBackground) {
445 var templateCardBackground = (template && typeof template["card-background"] === "string") ? template["card-background"] : "";439 var templateCardBackground;
440 if (template && typeof template["card-background"] === "string") {
441 templateCardBackground = 'decodeURI("' + encodeURI(template["card-background"]) + '")';
442 } else {
443 templateCardBackground = '""';
444 }
445
446 var backgroundElements0;446 var backgroundElements0;
447 var backgroundElements1;447 var backgroundElements1;
448 if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {448 if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
449 if (template["card-background"]["elements"][0] !== undefined) {449 var element0 = sanitizeColor(template["card-background"]["elements"][0]);
450 backgroundElements0 = '"%1"'.arg(template["card-background"]["elements"][0]);450 var element1 = sanitizeColor(template["card-background"]["elements"][1]);
451 if (element0 !== undefined) {
452 backgroundElements0 = '"%1"'.arg(element0);
451 }453 }
452 if (template["card-background"]["elements"][1] !== undefined) {454 if (element1 !== undefined) {
453 backgroundElements1 = '"%1"'.arg(template["card-background"]["elements"][1]);455 backgroundElements1 = '"%1"'.arg(element1);
454 }456 }
455 }457 }
456 code += kBackgroundLoaderCode.arg(templateCardBackground).arg(backgroundElements0).arg(backgroundElements1);458 code += kBackgroundLoaderCode.arg(backgroundElements0).arg(backgroundElements1).arg(templateCardBackground);
457 }459 }
458460
459 if (hasArt) {461 if (hasArt) {
@@ -479,9 +481,10 @@
479 }481 }
480482
481 var fallback = components["art"] && components["art"]["fallback"] || "";483 var fallback = components["art"] && components["art"]["fallback"] || "";
484 fallback = encodeURI(fallback);
482 var fallbackCode = "";485 var fallbackCode = "";
483 if (fallback !== "") {486 if (fallback !== "") {
484 fallbackCode += 'onStatusChanged: if (status === Image.Error) source = "%1";'.arg(fallback);487 fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
485 }488 }
486 code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(fallbackCode);489 code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(fallbackCode);
487 } else {490 } else {
@@ -576,9 +579,10 @@
576579
577 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';580 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
578 var fallback = components["mascot"] && components["mascot"]["fallback"] || "";581 var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
582 fallback = encodeURI(fallback);
579 var fallbackCode = "";583 var fallbackCode = "";
580 if (fallback !== "") {584 if (fallback !== "") {
581 fallbackCode += 'onStatusChanged: if (status === Image.Error) source = "%1";'.arg(fallback);585 fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
582 }586 }
583 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackCode);587 mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackCode);
584 }588 }
@@ -825,7 +829,7 @@
825 return code;829 return code;
826}830}
827831
828function createCardComponent(parent, template, components) {832function createCardComponent(parent, template, components, identifier) {
829 var imports = 'import QtQuick 2.4; \n\833 var imports = 'import QtQuick 2.4; \n\
830 import Ubuntu.Components 1.3; \n\834 import Ubuntu.Components 1.3; \n\
831 import Ubuntu.Settings.Components 0.1; \n\835 import Ubuntu.Settings.Components 0.1; \n\
@@ -835,7 +839,7 @@
835 var code = imports + 'Component {\n' + card + '}\n';839 var code = imports + 'Component {\n' + card + '}\n';
836840
837 try {841 try {
838 return Qt.createQmlObject(code, parent, "createCardComponent");842 return Qt.createQmlObject(code, parent, identifier);
839 } catch (e) {843 } catch (e) {
840 console.error("ERROR: Invalid component created.");844 console.error("ERROR: Invalid component created.");
841 console.error("Template:");845 console.error("Template:");
842846
=== modified file 'plugins/Dash/CardCreatorCache.qml'
--- plugins/Dash/CardCreatorCache.qml 2015-07-15 15:07:19 +0000
+++ plugins/Dash/CardCreatorCache.qml 2016-02-11 14:27:32 +0000
@@ -32,7 +32,7 @@
32 var allString = tString + cString;32 var allString = tString + cString;
33 var component = cache[allString];33 var component = cache[allString];
34 if (component === undefined) {34 if (component === undefined) {
35 component = CardCreator.createCardComponent(root, template, components);35 component = CardCreator.createCardComponent(root, template, components, allString);
36 cache[allString] = component;36 cache[allString] = component;
37 }37 }
38 return component;38 return component;
3939
=== modified file 'plugins/Dash/listviewwithpageheader.cpp'
--- plugins/Dash/listviewwithpageheader.cpp 2016-01-05 09:24:58 +0000
+++ plugins/Dash/listviewwithpageheader.cpp 2016-02-11 14:27:32 +0000
@@ -219,13 +219,7 @@
219 Q_FOREACH(ListItem *item, m_visibleItems)219 Q_FOREACH(ListItem *item, m_visibleItems)
220 releaseItem(item);220 releaseItem(item);
221 m_visibleItems.clear();221 m_visibleItems.clear();
222 m_firstVisibleIndex = -1;222 initializeValuesForEmptyList();
223 adjustMinYExtent();
224 setContentY(0);
225 m_clipItem->setY(0);
226 if (m_topSectionItem) {
227 QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
228 }
229223
230 m_delegateModel->setDelegate(delegate);224 m_delegateModel->setDelegate(delegate);
231225
@@ -236,6 +230,17 @@
236 }230 }
237}231}
238232
233void ListViewWithPageHeader::initializeValuesForEmptyList()
234{
235 m_firstVisibleIndex = -1;
236 adjustMinYExtent();
237 setContentY(0);
238 m_clipItem->setY(0);
239 if (m_topSectionItem) {
240 QQuickItemPrivate::get(m_topSectionItem)->setCulled(true);
241 }
242}
243
239QQuickItem *ListViewWithPageHeader::header() const244QQuickItem *ListViewWithPageHeader::header() const
240{245{
241 return m_headerItem;246 return m_headerItem;
@@ -835,7 +840,7 @@
835 }840 }
836 }841 }
837 if (!foundVisible) {842 if (!foundVisible) {
838 m_firstVisibleIndex = -1;843 initializeValuesForEmptyList();
839 }844 }
840 if (m_firstVisibleIndex != oldFirstVisibleIndex) {845 if (m_firstVisibleIndex != oldFirstVisibleIndex) {
841 adjustMinYExtent();846 adjustMinYExtent();
@@ -1092,7 +1097,11 @@
1092 }1097 }
10931098
1094 if (m_firstVisibleIndex != oldFirstVisibleIndex) {1099 if (m_firstVisibleIndex != oldFirstVisibleIndex) {
1095 adjustMinYExtent();1100 if (m_visibleItems.isEmpty()) {
1101 initializeValuesForEmptyList();
1102 } else {
1103 adjustMinYExtent();
1104 }
1096 }1105 }
10971106
1098 for (int i = 0; i < m_visibleItems.count(); ++i) {1107 for (int i = 0; i < m_visibleItems.count(); ++i) {
10991108
=== modified file 'plugins/Dash/listviewwithpageheader.h'
--- plugins/Dash/listviewwithpageheader.h 2015-11-26 13:50:56 +0000
+++ plugins/Dash/listviewwithpageheader.h 2016-02-11 14:27:32 +0000
@@ -168,6 +168,7 @@
168 QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);168 QQuickItem *getSectionItem(int modelIndex, bool alreadyInserted);
169 QQuickItem *getSectionItem(const QString &sectionText);169 QQuickItem *getSectionItem(const QString &sectionText);
170 void updateSectionItem(int modelIndex);170 void updateSectionItem(int modelIndex);
171 void initializeValuesForEmptyList();
171172
172 QQmlDelegateModel *m_delegateModel;173 QQmlDelegateModel *m_delegateModel;
173174
174175
=== added directory 'plugins/UInput'
=== added file 'tests/plugins/Dash/cardcreator/10.res'
--- tests/plugins/Dash/cardcreator/10.res 1970-01-01 00:00:00 +0000
+++ tests/plugins/Dash/cardcreator/10.res 2016-02-11 14:27:32 +0000
@@ -0,0 +1,142 @@
1AbstractButton {
2 id: root;
3 property var components;
4 property var cardData;
5 property string artShapeStyle: "inset";
6 property string backgroundShapeStyle: "inset";
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
9 property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
13 property bool asynchronous: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
16 enabled: true;
17
18Loader {
19 id: backgroundLoader;
20 objectName: "backgroundLoader";
21 anchors.fill: parent;
22 asynchronous: root.asynchronous;
23 visible: status == Loader.Ready;
24 sourceComponent: UbuntuShape {
25 objectName: "background";
26 radius: "medium";
27 aspect: {
28 switch (root.backgroundShapeStyle) {
29 case "inset": return UbuntuShape.Inset;
30 case "shadow": return UbuntuShape.DropShadow;
31 default:
32 case "flat": return UbuntuShape.Flat;
33 }
34 }
35 backgroundColor: getColor(0) || "white";
36 secondaryBackgroundColor: getColor(1) || backgroundColor;
37 backgroundMode: UbuntuShape.VerticalGradient;
38 anchors.fill: parent;
39 source: backgroundImage.source ? backgroundImage : null;
40 property real luminance: Style.luminance(backgroundColor);
41 property Image backgroundImage: Image {
42 objectName: "backgroundImage";
43 source: {
44 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
45 else return "";
46 }
47 }
48 function getColor(index) {
49 if (cardData && typeof cardData["background"] === "object"
50 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
51 return cardData["background"]["elements"][index];
52 } else return index === 0 ? "" : "";
53 }
54 }
55 }
56readonly property size artShapeSize: Qt.size(-1, -1);
57readonly property int headerHeight: row.height;
58Row {
59 id: row;
60 objectName: "outerRow";
61 property real margins: units.gu(1);
62 spacing: margins;
63 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
64 anchors { top: parent.top;
65 topMargin: units.gu(1);
66left: parent.left;
67 }
68 anchors.right: parent.right;
69 anchors.margins: margins;
70 anchors.rightMargin: 0;
71 data: [
72 CroppedImageMinimumSourceSize {
73 id: mascotImage;
74 objectName: "mascotImage";
75 anchors { verticalCenter: parent.verticalCenter; }
76 source: cardData && cardData["mascot"] || "";
77 width: units.gu(6);
78 height: units.gu(5.625);
79 horizontalAlignment: Image.AlignHCenter;
80 verticalAlignment: Image.AlignVCenter;
81 visible: showHeader;
82
83 }
84,Item {
85 id: headerTitleContainer;
86 anchors { verticalCenter: parent.verticalCenter; }
87 width: parent.width - x;
88 implicitHeight: titleLabel.height + subtitleLabel.height;
89 data: [
90 Label {
91 id: titleLabel;
92 objectName: "titleLabel";
93 anchors { right: parent.right;
94rightMargin: units.gu(1);
95left: parent.left;
96 top: parent.top; }
97 elide: Text.ElideRight;
98 fontSize: "small";
99 wrapMode: Text.Wrap;
100 maximumLineCount: 2;
101 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
102 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");
103 visible: showHeader ;
104 width: undefined;
105 text: root.title;
106 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
107 horizontalAlignment: root.titleAlignment;
108 }
109,Label {
110 id: subtitleLabel;
111 objectName: "subtitleLabel";
112 anchors { right: parent.right;
113 left: parent.left;
114rightMargin: units.gu(1);
115top: titleLabel.bottom;
116 }
117 anchors.topMargin: units.dp(2);
118 elide: Text.ElideRight;
119 maximumLineCount: 1;
120 fontSize: "x-small";
121 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
122 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");
123 visible: titleLabel.visible && titleLabel.text;
124 text: cardData && cardData["subtitle"] || "";
125 font.weight: Font.Light;
126 }
127
128 ]
129 }
130
131 ]
132 }
133UbuntuShape {
134 id: touchdown;
135 objectName: "touchdown";
136 anchors { fill: backgroundLoader }
137 visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed;
138 radius: "medium";
139 borderSource: "radius_pressed.sci"
140 }
141implicitHeight: row.y + row.height + units.gu(1);
142}
0143
=== modified file 'tests/plugins/Dash/cardcreator/10.tst'
--- tests/plugins/Dash/cardcreator/10.tst 2016-01-21 22:33:46 +0000
+++ tests/plugins/Dash/cardcreator/10.tst 2016-02-11 14:27:32 +0000
@@ -1,2 +1,3 @@
1template: {"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 ? \"" ] } }1template: {"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 () { \"" ] } }
2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}}2components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}}
3result: 10.res
3\ No newline at end of file4\ No newline at end of file
45
=== added file 'tests/plugins/Dash/cardcreator/11.res'
--- tests/plugins/Dash/cardcreator/11.res 1970-01-01 00:00:00 +0000
+++ tests/plugins/Dash/cardcreator/11.res 2016-02-11 14:27:32 +0000
@@ -0,0 +1,217 @@
1AbstractButton {
2 id: root;
3 property var components;
4 property var cardData;
5 property string artShapeStyle: "inset";
6 property string backgroundShapeStyle: "inset";
7 property real fontScale: 1.0;
8 property var scopeStyle: null;
9 property int titleAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
13 property bool asynchronous: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
16 enabled: true;
17
18Loader {
19 id: backgroundLoader;
20 objectName: "backgroundLoader";
21 anchors.fill: parent;
22 asynchronous: root.asynchronous;
23 visible: status == Loader.Ready;
24 sourceComponent: UbuntuShape {
25 objectName: "background";
26 radius: "medium";
27 aspect: {
28 switch (root.backgroundShapeStyle) {
29 case "inset": return UbuntuShape.Inset;
30 case "shadow": return UbuntuShape.DropShadow;
31 default:
32 case "flat": return UbuntuShape.Flat;
33 }
34 }
35 backgroundColor: getColor(0) || "white";
36 secondaryBackgroundColor: getColor(1) || backgroundColor;
37 backgroundMode: UbuntuShape.VerticalGradient;
38 anchors.fill: parent;
39 source: backgroundImage.source ? backgroundImage : null;
40 property real luminance: Style.luminance(backgroundColor);
41 property Image backgroundImage: Image {
42 objectName: "backgroundImage";
43 source: {
44 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
45 else return "";
46 }
47 }
48 function getColor(index) {
49 if (cardData && typeof cardData["background"] === "object"
50 && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
51 return cardData["background"]["elements"][index];
52 } else return index === 0 ? "#E9E9E9" : undefined;
53 }
54 }
55 }
56readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
57Item {
58 id: artShapeHolder;
59 height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
60 width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
61 anchors { horizontalCenter: parent.horizontalCenter; }
62 Loader {
63 id: artShapeLoader;
64 objectName: "artShapeLoader";
65 active: cardData && cardData["art"] || false;
66 asynchronous: root.asynchronous;
67 visible: status == Loader.Ready;
68 sourceComponent: Item {
69 id: artShape;
70 objectName: "artShape";
71 readonly property bool doShapeItem: components["art"]["conciergeMode"] !== true;
72 visible: image.status == Image.Ready;
73 readonly property alias image: artImage;
74 ShaderEffectSource {
75 id: artShapeSource;
76 sourceItem: artImage;
77 anchors.centerIn: parent;
78 width: 1;
79 height: 1;
80 hideSource: doShapeItem;
81 }
82 Loader {
83 anchors.fill: parent;
84 visible: artShape.doShapeItem;
85 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent;
86 Component {
87 id: artShapeShapeComponent;
88 UbuntuShape {
89 source: artShapeSource;
90 sourceFillMode: UbuntuShape.PreserveAspectCrop;
91 radius: "medium";
92 aspect: {
93 switch (root.artShapeStyle) {
94 case "inset": return UbuntuShape.Inset;
95 case "shadow": return UbuntuShape.DropShadow;
96 default:
97 case "flat": return UbuntuShape.Flat;
98 }
99 }
100 }
101 }
102 Component {
103 id: artShapeIconComponent;
104 ProportionalShape { source: artShapeSource; aspect: UbuntuShape.DropShadow; }
105 }
106 }
107 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
108 readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
109 Component.onCompleted: { updateWidthHeightBindings(); }
110 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
111 function updateWidthHeightBindings() {
112 if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
113 width = root.fixedArtShapeSize.width;
114 height = root.fixedArtShapeSize.height;
115 } else {
116 width = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.width });
117 height = Qt.binding(function() { return image.status !== Image.Ready ? 0 : image.height });
118 }
119 }
120 CroppedImageMinimumSourceSize {
121 id: artImage;
122 objectName: "artImage";
123 source: cardData && cardData["art"] || "";
124 asynchronous: root.asynchronous;
125 width: root.width;
126 height: width / artShape.aspect;
127 onStatusChanged: if (status === Image.Error) source = decodeURI("%5C");
128 }
129 }
130 }
131 }
132readonly property int headerHeight: row.height;
133Row {
134 id: row;
135 objectName: "outerRow";
136 property real margins: units.gu(1);
137 spacing: margins;
138 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
139 anchors { top: artShapeHolder.bottom;
140 topMargin: units.gu(1);
141left: parent.left;
142 }
143 anchors.right: parent.right;
144 anchors.margins: margins;
145 anchors.rightMargin: 0;
146 data: [
147 CroppedImageMinimumSourceSize {
148 id: mascotImage;
149 objectName: "mascotImage";
150 anchors { verticalCenter: parent.verticalCenter; }
151 source: cardData && cardData["mascot"] || "";
152 width: units.gu(6);
153 height: units.gu(5.625);
154 horizontalAlignment: Image.AlignHCenter;
155 verticalAlignment: Image.AlignVCenter;
156 visible: showHeader;
157 onStatusChanged: if (status === Image.Error) source = decodeURI("%22");
158 }
159,Item {
160 id: headerTitleContainer;
161 anchors { verticalCenter: parent.verticalCenter; }
162 width: parent.width - x;
163 implicitHeight: titleLabel.height + subtitleLabel.height;
164 data: [
165 Label {
166 id: titleLabel;
167 objectName: "titleLabel";
168 anchors { right: parent.right;
169rightMargin: units.gu(1);
170left: parent.left;
171 top: parent.top; }
172 elide: Text.ElideRight;
173 fontSize: "small";
174 wrapMode: Text.Wrap;
175 maximumLineCount: 2;
176 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
177 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");
178 visible: showHeader ;
179 width: undefined;
180 text: root.title;
181 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
182 horizontalAlignment: root.titleAlignment;
183 }
184,Label {
185 id: subtitleLabel;
186 objectName: "subtitleLabel";
187 anchors { right: parent.right;
188 left: parent.left;
189rightMargin: units.gu(1);
190top: titleLabel.bottom;
191 }
192 anchors.topMargin: units.dp(2);
193 elide: Text.ElideRight;
194 maximumLineCount: 1;
195 fontSize: "x-small";
196 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
197 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");
198 visible: titleLabel.visible && titleLabel.text;
199 text: cardData && cardData["subtitle"] || "";
200 font.weight: Font.Light;
201 }
202
203 ]
204 }
205
206 ]
207 }
208UbuntuShape {
209 id: touchdown;
210 objectName: "touchdown";
211 anchors { fill: backgroundLoader }
212 visible: root.artShapeStyle != "shadow" && root.artShapeStyle != "icon" && root.pressed;
213 radius: "medium";
214 borderSource: "radius_pressed.sci"
215 }
216implicitHeight: row.y + row.height + units.gu(1);
217}
0218
=== added file 'tests/plugins/Dash/cardcreator/11.tst'
--- tests/plugins/Dash/cardcreator/11.tst 1970-01-01 00:00:00 +0000
+++ tests/plugins/Dash/cardcreator/11.tst 2016-02-11 14:27:32 +0000
@@ -0,0 +1,3 @@
1template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2 }
2components: {"art":{"aspect-ratio":1,"field":"art","fallback":"\\"},"background":{"field":"background"},"mascot":{"field":"icon","fallback":"\""},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}}
3result: 11.res
0\ No newline at end of file4\ No newline at end of file
15
=== modified file 'tests/plugins/Dash/cardcreator/3.res'
--- tests/plugins/Dash/cardcreator/3.res 2016-01-21 22:44:41 +0000
+++ tests/plugins/Dash/cardcreator/3.res 2016-02-11 14:27:32 +0000
@@ -86,7 +86,7 @@
86 asynchronous: root.asynchronous;86 asynchronous: root.asynchronous;
87 width: root.width;87 width: root.width;
88 height: width / artShape.aspect;88 height: width / artShape.aspect;
89 onStatusChanged: if (status === Image.Error) source = "IHAVE\"ESCAPED\"QUOTES\"";89 onStatusChanged: if (status === Image.Error) source = decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
90 }90 }
91 } 91 }
92 } 92 }
9393
=== modified file 'tests/plugins/Dash/cardcreator/6.res'
--- tests/plugins/Dash/cardcreator/6.res 2015-11-20 16:33:38 +0000
+++ tests/plugins/Dash/cardcreator/6.res 2016-02-11 14:27:32 +0000
@@ -42,7 +42,7 @@
42 objectName: "backgroundImage"; 42 objectName: "backgroundImage";
43 source: { 43 source: {
44 if (cardData && typeof cardData["background"] === "string") return cardData["background"]; 44 if (cardData && typeof cardData["background"] === "string") return cardData["background"];
45 else return "http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png";45 else return decodeURI("http://assets.ubuntu.com/sites/ubuntu/latest/u/img/logos/logo-ubuntu-grey.png");
46 } 46 }
47 } 47 }
48 function getColor(index) { 48 function getColor(index) {
4949
=== modified file 'tests/plugins/Dash/cardcreatortest.cpp'
--- tests/plugins/Dash/cardcreatortest.cpp 2016-01-21 22:33:46 +0000
+++ tests/plugins/Dash/cardcreatortest.cpp 2016-02-11 14:27:32 +0000
@@ -63,6 +63,7 @@
6363
64 QVERIFY(lines[0].startsWith(templateString));64 QVERIFY(lines[0].startsWith(templateString));
65 QVERIFY(lines[1].startsWith(componentsString));65 QVERIFY(lines[1].startsWith(componentsString));
66 QVERIFY(lines[2].startsWith(resultString));
6667
67 const QString templateJSON = lines[0].mid(templateString.length());68 const QString templateJSON = lines[0].mid(templateString.length());
68 const QString componentsJSON = lines[1].mid(componentsString.length());69 const QString componentsJSON = lines[1].mid(componentsString.length());
@@ -71,32 +72,30 @@
71 QVariant cardStringResult;72 QVariant cardStringResult;
72 QMetaObject::invokeMethod(view->rootObject(), "cardString", Q_RETURN_ARG(QVariant, cardStringResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));73 QMetaObject::invokeMethod(view->rootObject(), "cardString", Q_RETURN_ARG(QVariant, cardStringResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
7374
74 if (!resultFileName.isEmpty()) {75 QFile testResultFile(testDirPath + resultFileName);
75 QFile testResultFile(testDirPath + resultFileName);76 QVERIFY(testResultFile.open(QIODevice::ReadOnly));
76 QVERIFY(testResultFile.open(QIODevice::ReadOnly));77 QTextStream ts2(&testResultFile);
77 QTextStream ts2(&testResultFile);78
7879 // Record failed results to /tmp
79 // Record failed results to /tmp80 const QString executedResult = cardStringResult.toString();
80 const QString executedResult = cardStringResult.toString();81 QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX");
81 QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX");82 tmpFile.open();
82 tmpFile.open();83 tmpFile.setAutoRemove(false);
83 tmpFile.setAutoRemove(false);84 tmpFile.write(executedResult.toUtf8().constData());
84 tmpFile.write(executedResult.toUtf8().constData());85
8586 // Line by line comparison
86 // Line by line comparison87 const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
87 const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");88 const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
88 const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");89 for (int i = 0; i < expectedLines.size(); ++i) {
89 for (int i = 0; i < expectedLines.size(); ++i) {90 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
90 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
91 }
92
93 // Remove the result if it passed
94 tmpFile.setAutoRemove(true);
95 }91 }
9692
93 // Remove the result if it passed
94 tmpFile.setAutoRemove(true);
95
97 QVariant createCardComponentResult;96 QVariant createCardComponentResult;
98 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));97 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
99 QCOMPARE(createCardComponentResult.toBool(), !resultFileName.isEmpty());98 QVERIFY(createCardComponentResult.toBool());
100 }99 }
101 }100 }
102101
103102
=== modified file 'tests/plugins/Dash/listviewwithpageheadertest.cpp'
--- tests/plugins/Dash/listviewwithpageheadertest.cpp 2015-09-02 08:04:41 +0000
+++ tests/plugins/Dash/listviewwithpageheadertest.cpp 2016-02-11 14:27:32 +0000
@@ -1954,6 +1954,29 @@
1954 QCOMPARE(lvwph->m_firstVisibleIndex, 0);1954 QCOMPARE(lvwph->m_firstVisibleIndex, 0);
1955 }1955 }
19561956
1957 void testBug1540490()
1958 {
1959 lvwph->header()->setImplicitHeight(150);
1960 verifyItem(0, 150., 150., false);
1961
1962 QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 3), Q_ARG(QVariant, 3));
1963 model->setProperty(0, "size", 400);
1964 model->setProperty(1, "size", 600);
1965 model->setProperty(2, "size", 300);
1966
1967 scrollToBottom();
1968 changeContentY(-200);
1969
1970 QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 2));
1971 model->setProperty(0, "size", 100);
1972 QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 1));
1973 lvwph->header()->setImplicitHeight(50);
1974 QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 200));
1975
1976 QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
1977 verifyItem(0, 50., 200., false);
1978 }
1979
1957private:1980private:
1958 QQuickView *view;1981 QQuickView *view;
1959 ListViewWithPageHeader *lvwph;1982 ListViewWithPageHeader *lvwph;
19601983
=== modified file 'tests/plugins/Dash/listviewwithpageheadertest.qml'
--- tests/plugins/Dash/listviewwithpageheadertest.qml 2015-07-15 15:07:19 +0000
+++ tests/plugins/Dash/listviewwithpageheadertest.qml 2016-02-11 14:27:32 +0000
@@ -85,7 +85,6 @@
85 pageHeader: Rectangle {85 pageHeader: Rectangle {
86 color: "transparent"86 color: "transparent"
87 width: parent.width87 width: parent.width
88 height: 50
89 implicitHeight: 5088 implicitHeight: 50
90 Text {89 Text {
91 anchors.fill: parent90 anchors.fill: parent

Subscribers

People subscribed via source and target branches

to all changes: