on xenial help sign in
Merge fallback for empty
dc0885dAlbert Astals Cid
plugins/Dash/CardCreator.js+25−14
@@ -61,9 +61,10 @@ var kBackgroundLoaderCode = 'Loader {\n\
61// %1 is used as anchors of artShapeHolder
61// %1 is used as anchors of artShapeHolder
62// %2 is used as image width
62// %2 is used as image width
63// %3 is used as image height
63// %3 is used as image height
64−// %4 is used as aspect ratio fallback
65−// %5 is used for artShapeSource.hideSource and inner Loader visible
64+// %4 is used for artShapeSource.hideSource and inner Loader visible
65+// %5 is used as aspect ratio fallback
66// %6 is injected as code to artImage
66// %6 is injected as code to artImage
67+// %7 is used as image fallback
67var kArtShapeHolderCode = 'Item { \n\
68var kArtShapeHolderCode = 'Item { \n\
68 id: artShapeHolder; \n\
69 id: artShapeHolder; \n\
69 height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
70 height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
@@ -72,7 +73,8 @@ var kArtShapeHolderCode = 'Item { \n\
72 Loader { \n\
73 Loader { \n\
73 id: artShapeLoader; \n\
74 id: artShapeLoader; \n\
74 objectName: "artShapeLoader"; \n\
75 objectName: "artShapeLoader"; \n\
75− active: cardData && cardData["art"] || false; \n\
76+ readonly property string cardArt: cardData && cardData["art"] || %7; \n\
77+ active: cardArt != ""; \n\
76 asynchronous: root.asynchronous; \n\
78 asynchronous: root.asynchronous; \n\
77 visible: status == Loader.Ready; \n\
79 visible: status == Loader.Ready; \n\
78 sourceComponent: Item { \n\
80 sourceComponent: Item { \n\
@@ -86,11 +88,11 @@ var kArtShapeHolderCode = 'Item { \n\
86 anchors.centerIn: parent; \n\
88 anchors.centerIn: parent; \n\
87 width: 1; \n\
89 width: 1; \n\
88 height: 1; \n\
90 height: 1; \n\
89− hideSource: %5; \n\
91+ hideSource: %4; \n\
90 } \n\
92 } \n\
91 Loader { \n\
93 Loader { \n\
92 anchors.fill: parent; \n\
94 anchors.fill: parent; \n\
93− visible: %5; \n\
95+ visible: %4; \n\
94 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent; \n\
96 sourceComponent: root.artShapeStyle === "icon" ? artShapeIconComponent : artShapeShapeComponent; \n\
95 Component { \n\
97 Component { \n\
96 id: artShapeShapeComponent; \n\
98 id: artShapeShapeComponent; \n\
@@ -114,7 +116,7 @@ var kArtShapeHolderCode = 'Item { \n\
114 } \n\
116 } \n\
115 } \n\
117 } \n\
116 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
118 readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1; \n\
117− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : %4; \n\
119+ readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : %5; \n\
118 Component.onCompleted: { updateWidthHeightBindings(); } \n\
120 Component.onCompleted: { updateWidthHeightBindings(); } \n\
119 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); } \n\
121 Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); } \n\
120 function updateWidthHeightBindings() { \n\
122 function updateWidthHeightBindings() { \n\
@@ -129,7 +131,7 @@ var kArtShapeHolderCode = 'Item { \n\
129 CroppedImageMinimumSourceSize { \n\
131 CroppedImageMinimumSourceSize { \n\
130 id: artImage; \n\
132 id: artImage; \n\
131 objectName: "artImage"; \n\
133 objectName: "artImage"; \n\
132− source: cardData && cardData["art"] || ""; \n\
134+ source: artShapeLoader.cardArt; \n\
133 asynchronous: root.asynchronous; \n\
135 asynchronous: root.asynchronous; \n\
134 width: %2; \n\
136 width: %2; \n\
135 height: %3; \n\
137 height: %3; \n\
@@ -246,11 +248,12 @@ var kMascotShapeLoaderCode = 'Loader { \n\
246// %1 is used as anchors of mascotImage
248// %1 is used as anchors of mascotImage
247// %2 is used as visible of mascotImage
249// %2 is used as visible of mascotImage
248// %3 is injected as code to mascotImage
250// %3 is injected as code to mascotImage
251+// %4 is used as fallback image
249var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
252var kMascotImageCode = 'CroppedImageMinimumSourceSize { \n\
250 id: mascotImage; \n\
253 id: mascotImage; \n\
251 objectName: "mascotImage"; \n\
254 objectName: "mascotImage"; \n\
252 anchors { %1 } \n\
255 anchors { %1 } \n\
253− source: cardData && cardData["mascot"] || ""; \n\
256+ source: cardData && cardData["mascot"] || %4; \n\
254 width: units.gu(6); \n\
257 width: units.gu(6); \n\
255 height: units.gu(5.625); \n\
258 height: units.gu(5.625); \n\
256 horizontalAlignment: Image.AlignHCenter; \n\
259 horizontalAlignment: Image.AlignHCenter; \n\
@@ -487,11 +490,15 @@ function cardString(template, components) {
487 }
490 }
488 var fallback = components["art"] && components["art"]["fallback"] || "";
491 var fallback = components["art"] && components["art"]["fallback"] || "";
489 fallback = encodeURI(fallback);
492 fallback = encodeURI(fallback);
490− var fallbackCode = "";
493+ var fallbackStatusCode = "";
494+ var fallbackURICode = '""';
491 if (fallback !== "") {
495 if (fallback !== "") {
496+ // fallbackStatusCode has %6 in it because we want to substitute it for fallbackURICode
497+ // which in kArtShapeHolderCode is %7
492− fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
498+ fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %7;';
499+ fallbackURICode = 'decodeURI("%1")'.arg(fallback);
493 }
500 }
494− code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(aspectRatio).arg(isConciergeMode ? "false" : "true").arg(fallbackCode);
501+ code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode).arg(isConciergeMode ? "false" : "true").arg(aspectRatio).arg(fallbackStatusCode).arg(fallbackURICode);
495 } else {
502 } else {
496 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
503 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
497 }
504 }
@@ -585,11 +592,15 @@ function cardString(template, components) {
585 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
592 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
586 var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
593 var fallback = components["mascot"] && components["mascot"]["fallback"] || "";
587 fallback = encodeURI(fallback);
594 fallback = encodeURI(fallback);
588− var fallbackCode = "";
595+ var fallbackStatusCode = "";
596+ var fallbackURICode = '""';
589 if (fallback !== "") {
597 if (fallback !== "") {
598+ // fallbackStatusCode has %4 in it because we want to substitute it for fallbackURICode
599+ // which in kMascotImageCode is %4
590− fallbackCode += 'onStatusChanged: if (status === Image.Error) source = decodeURI("%1");'.arg(fallback);
600+ fallbackStatusCode += 'onStatusChanged: if (status === Image.Error) source = %4;';
601+ fallbackURICode = 'decodeURI("%1")'.arg(fallback);
591 }
602 }
592− mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackCode);
603+ mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible).arg(fallbackStatusCode).arg(fallbackURICode);
593 }
604 }
594
605
595 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white")';
606 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && root.scopeStyle ? root.scopeStyle.getTextColor(backgroundLoader.item.luminance) : (backgroundLoader.item && backgroundLoader.item.luminance > 0.7 ? theme.palette.normal.baseText : "white")';
qml/Dash/Previews/PreviewHeader.qml+1−1
@@ -35,7 +35,7 @@ PreviewWidget {
35 Item {
35 Item {
36 id: headerRoot
36 id: headerRoot
37 objectName: "innerPreviewHeader"
37 objectName: "innerPreviewHeader"
38− readonly property url mascot: root.widgetData["mascot"] || ""
38+ readonly property url mascot: root.widgetData["mascot"] || fallback
39 readonly property url fallback: root.widgetData["fallback"] || ""
39 readonly property url fallback: root.widgetData["fallback"] || ""
40 readonly property string title: root.widgetData["title"] || ""
40 readonly property string title: root.widgetData["title"] || ""
41 readonly property string subtitle: root.widgetData["subtitle"] || ""
41 readonly property string subtitle: root.widgetData["subtitle"] || ""
qml/Dash/Previews/PreviewImageGallery.qml+2−2
@@ -59,7 +59,7 @@ PreviewWidget {
59 top: parent.top
59 top: parent.top
60 bottom: parent.bottom
60 bottom: parent.bottom
61 }
61 }
62− source: modelData ? modelData : ""
62+ source: modelData || root.widgetData["fallback"] || ""
63 scaleTo: "height"
63 scaleTo: "height"
64 initialWidth: units.gu(13)
64 initialWidth: units.gu(13)
65 pressed: mouseArea.pressed
65 pressed: mouseArea.pressed
@@ -128,7 +128,7 @@ PreviewWidget {
128 bottom: parent.bottom
128 bottom: parent.bottom
129 }
129 }
130 width: overlay.width
130 width: overlay.width
131− source: modelData ? modelData : ""
131+ source: modelData || root.widgetData["fallback"] || ""
132 fillMode: Image.PreserveAspectFit
132 fillMode: Image.PreserveAspectFit
133 sourceSize { width: screenshot.width; height: screenshot.height }
133 sourceSize { width: screenshot.width; height: screenshot.height }
134
134
qml/Dash/Previews/PreviewZoomableImage.qml+2−2
@@ -42,7 +42,7 @@ PreviewWidget {
42 right: parent.right
42 right: parent.right
43 }
43 }
44 scaleTo: "width"
44 scaleTo: "width"
45− source: widgetData["source"]
45+ source: widgetData["source"] || widgetData["fallback"] || ""
46 asynchronous: true
46 asynchronous: true
47 useUbuntuShape: false
47 useUbuntuShape: false
48 pressed: mouseArea.pressed
48 pressed: mouseArea.pressed
@@ -86,7 +86,7 @@ PreviewWidget {
86
86
87 delegate: ZoomableImage {
87 delegate: ZoomableImage {
88 anchors.fill: parent
88 anchors.fill: parent
89− source: widgetData["source"]
89+ source: widgetData["source"] || widgetData["fallback"] || ""
90 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
90 zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
91 // If modelData would change after failing to load it would not be
91 // If modelData would change after failing to load it would not be
92 // reloaded since the source binding is destroyed by the source = fallback
92 // reloaded since the source binding is destroyed by the source = fallback
tests/plugins/Dash/cardcreator/1.res+3−2
@@ -23,7 +23,8 @@ Item {
23 Loader {
23 Loader {
24 id: artShapeLoader;
24 id: artShapeLoader;
25 objectName: "artShapeLoader";
25 objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || "";
27+ active: cardArt != "";
27 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
28 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
29 sourceComponent: Item {
30 sourceComponent: Item {
@@ -80,7 +81,7 @@ Item {
80 CroppedImageMinimumSourceSize {
81 CroppedImageMinimumSourceSize {
81 id: artImage;
82 id: artImage;
82 objectName: "artImage";
83 objectName: "artImage";
83− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
84 asynchronous: root.asynchronous;
85 asynchronous: root.asynchronous;
85 width: root.width;
86 width: root.width;
86 height: width / artShape.aspect;
87 height: width / artShape.aspect;
tests/plugins/Dash/cardcreator/11.res+4−3
@@ -61,7 +61,8 @@ Item {
61 Loader {
61 Loader {
62 id: artShapeLoader;
62 id: artShapeLoader;
63 objectName: "artShapeLoader";
63 objectName: "artShapeLoader";
64− active: cardData && cardData["art"] || false;
64+ readonly property string cardArt: cardData && cardData["art"] || decodeURI("%5C");
65+ active: cardArt != "";
65 asynchronous: root.asynchronous;
66 asynchronous: root.asynchronous;
66 visible: status == Loader.Ready;
67 visible: status == Loader.Ready;
67 sourceComponent: Item {
68 sourceComponent: Item {
@@ -118,7 +119,7 @@ Item {
118 CroppedImageMinimumSourceSize {
119 CroppedImageMinimumSourceSize {
119 id: artImage;
120 id: artImage;
120 objectName: "artImage";
121 objectName: "artImage";
121− source: cardData && cardData["art"] || "";
122+ source: artShapeLoader.cardArt;
122 asynchronous: root.asynchronous;
123 asynchronous: root.asynchronous;
123 width: root.width;
124 width: root.width;
124 height: width / artShape.aspect;
125 height: width / artShape.aspect;
@@ -146,7 +147,7 @@ left: parent.left;
146 id: mascotImage;
147 id: mascotImage;
147 objectName: "mascotImage";
148 objectName: "mascotImage";
148 anchors { verticalCenter: parent.verticalCenter; }
149 anchors { verticalCenter: parent.verticalCenter; }
149− source: cardData && cardData["mascot"] || "";
150+ source: cardData && cardData["mascot"] || decodeURI("%22");
150 width: units.gu(6);
151 width: units.gu(6);
151 height: units.gu(5.625);
152 height: units.gu(5.625);
152 horizontalAlignment: Image.AlignHCenter;
153 horizontalAlignment: Image.AlignHCenter;
tests/plugins/Dash/cardcreator/3.res+3−2
@@ -23,7 +23,8 @@ Item {
23 Loader {
23 Loader {
24 id: artShapeLoader;
24 id: artShapeLoader;
25 objectName: "artShapeLoader";
25 objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || decodeURI("IHAVE%5C%22ESCAPED%5C%22QUOTES%5C%22");
27+ active: cardArt != "";
27 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
28 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
29 sourceComponent: Item {
30 sourceComponent: Item {
@@ -80,7 +81,7 @@ Item {
80 CroppedImageMinimumSourceSize {
81 CroppedImageMinimumSourceSize {
81 id: artImage;
82 id: artImage;
82 objectName: "artImage";
83 objectName: "artImage";
83− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
84 asynchronous: root.asynchronous;
85 asynchronous: root.asynchronous;
85 width: root.width;
86 width: root.width;
86 height: width / artShape.aspect;
87 height: width / artShape.aspect;
tests/plugins/Dash/cardcreator/5.res+3−2
@@ -23,7 +23,8 @@ Item {
23 Loader {
23 Loader {
24 id: artShapeLoader;
24 id: artShapeLoader;
25 objectName: "artShapeLoader";
25 objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
26+ readonly property string cardArt: cardData && cardData["art"] || "";
27+ active: cardArt != "";
27 asynchronous: root.asynchronous;
28 asynchronous: root.asynchronous;
28 visible: status == Loader.Ready;
29 visible: status == Loader.Ready;
29 sourceComponent: Item {
30 sourceComponent: Item {
@@ -80,7 +81,7 @@ Item {
80 CroppedImageMinimumSourceSize {
81 CroppedImageMinimumSourceSize {
81 id: artImage;
82 id: artImage;
82 objectName: "artImage";
83 objectName: "artImage";
83− source: cardData && cardData["art"] || "";
84+ source: artShapeLoader.cardArt;
84 asynchronous: root.asynchronous;
85 asynchronous: root.asynchronous;
85 width: root.width;
86 width: root.width;
86 height: width / artShape.aspect;
87 height: width / artShape.aspect;
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml+15
@@ -54,6 +54,14 @@ Rectangle {
54 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
54 "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
55 }
55 }
56
56
57+ property var emptyfallbackheaderjson: {
58+ "title": "THE TITLE",
59+ "subtitle": "Something catchy",
60+ "mascot": "",
61+ "fallback": "../graphics/play_button.png",
62+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
63+ }
64+
57 PreviewHeader {
65 PreviewHeader {
58 id: previewHeader
66 id: previewHeader
59 widgetData: headerjson
67 widgetData: headerjson
@@ -150,6 +158,13 @@ Rectangle {
150 var mascot = findChild(previewHeader, "mascotShape");
158 var mascot = findChild(previewHeader, "mascotShape");
151 tryCompare(mascot, "visible", true);
159 tryCompare(mascot, "visible", true);
152 tryCompare(mascot.source, "status", Image.Ready);
160 tryCompare(mascot.source, "status", Image.Ready);
161+
162+ previewHeader.widgetData = {};
163+ previewHeader.widgetData = emptyfallbackheaderjson;
164+ tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
165+ var mascot = findChild(previewHeader, "mascotShape");
166+ tryCompare(mascot, "visible", true);
167+ tryCompare(mascot.source, "status", Image.Ready);
153 }
168 }
154 }
169 }
155}
170}
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+19
@@ -48,6 +48,16 @@ Rectangle {
48 , "fallback": "../../../tests/graphics/clock@18.png"
48 , "fallback": "../../../tests/graphics/clock@18.png"
49 }
49 }
50
50
51+ property var sourcesModelEmptyWithFallback: {
52+ "sources": [
53+ "../../graphics/phone_background.jpg",
54+ "../../graphics/tablet_background.jpg",
55+ "../../../tests/graphics/clock@18.png",
56+ ""
57+ ]
58+ , "fallback": "../../../tests/graphics/clock@18.png"
59+ }
60+
51 PreviewImageGallery {
61 PreviewImageGallery {
52 id: imageGallery
62 id: imageGallery
53 width: parent.width
63 width: parent.width
@@ -123,5 +133,14 @@ Rectangle {
123 image3 = findChild(imageGallery, "previewImage3");
133 image3 = findChild(imageGallery, "previewImage3");
124 tryCompare(image3, "state", "ready");
134 tryCompare(image3, "state", "ready");
125 }
135 }
136+
137+ function test_empty_fallback() {
138+ var image3 = findChild(imageGallery, "previewImage3");
139+ tryCompare(image3, "state", "error");
140+ imageGallery.widgetData = sourcesModel0;
141+ imageGallery.widgetData = sourcesModelEmptyWithFallback;
142+ image3 = findChild(imageGallery, "previewImage3");
143+ tryCompare(image3, "state", "ready");
144+ }
126 }
145 }
127}
146}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+12
@@ -39,6 +39,11 @@ Rectangle {
39 "fallback": "../../graphics/phone_background.jpg"
39 "fallback": "../../graphics/phone_background.jpg"
40 }
40 }
41
41
42+ property var widgetData3: {
43+ "source": "",
44+ "fallback": "../../graphics/phone_background.jpg"
45+ }
46+
42 Loader {
47 Loader {
43 id: loader
48 id: loader
44 width: parent.width
49 width: parent.width
@@ -100,5 +105,12 @@ Rectangle {
100 waitForRendering(lazyImage);
105 waitForRendering(lazyImage);
101 tryCompare(lazyImage, "state", "ready");
106 tryCompare(lazyImage, "state", "ready");
102 }
107 }
108+
109+ function test_emptyfallback() {
110+ zoomableImage.widgetData = widgetData3;
111+ waitForRendering(zoomableImage);
112+ waitForRendering(lazyImage);
113+ tryCompare(lazyImage, "state", "ready");
114+ }
103 }
115 }
104}
116}
tests/qmltests/Dash/tst_Card.qml+18
@@ -475,6 +475,15 @@ Rectangle {
475 card.cardDataChanged();
475 card.cardDataChanged();
476 waitForRendering(card);
476 waitForRendering(card);
477 tryCompare(art, "visible", true);
477 tryCompare(art, "visible", true);
478+ compare(artImage.source, Qt.resolvedUrl("artwork/emblem.png"));
479+
480+ cardTool.components["art"]["fallback"] = Qt.resolvedUrl("artwork/checkers.png");
481+ cardTool.componentsChanged();
482+ card.cardData["art"] = "";
483+ card.cardDataChanged();
484+ waitForRendering(card);
485+ tryCompare(art, "visible", true);
486+ compare(artImage.source, Qt.resolvedUrl("artwork/checkers.png"));
478
487
479 card.cardData["mascot"] = "somethingbroken2";
488 card.cardData["mascot"] = "somethingbroken2";
480 card.cardDataChanged();
489 card.cardDataChanged();
@@ -486,6 +495,15 @@ Rectangle {
486 card.cardDataChanged();
495 card.cardDataChanged();
487 waitForRendering(card);
496 waitForRendering(card);
488 tryCompare(mascotImage, "status", Image.Ready);
497 tryCompare(mascotImage, "status", Image.Ready);
498+ compare(mascotImage.source, Qt.resolvedUrl("artwork/emblem.png"));
499+
500+ cardTool.components["mascot"] = {"fallback": Qt.resolvedUrl("artwork/checkers.png")};
501+ cardTool.componentsChanged();
502+ card.cardData["mascot"] = "";
503+ card.cardDataChanged();
504+ waitForRendering(card);
505+ tryCompare(mascotImage, "status", Image.Ready);
506+ compare(mascotImage.source, Qt.resolvedUrl("artwork/checkers.png"));
489 }
507 }
490
508
491 function test_font_weights_data() {
509 function test_font_weights_data() {