on xenial help sign in
Add newlines, makes debugging the code when it fails easier
commit01b778c6ccef1867adf3b4ad03e94fc65a09a6de authorAlbert Astals <albert.astals@canonical.com> dateTue 6 May 2014, 15:09 (12 years ago) parent1ad655b
7 files changed+787−273
plugins/Dash/CardCreator.js+267−267 tests/plugins/Dash/cardcreator/1.res+79−1 tests/plugins/Dash/cardcreator/2.res+112−1 tests/plugins/Dash/cardcreator/3.res+97−1 tests/plugins/Dash/cardcreator/4.res+90−1 tests/plugins/Dash/cardcreator/5.res+140−1 tests/plugins/Dash/cardcreatortest.cpp+2−1
plugins/Dash/CardCreator.js+267−267
@@ -18,19 +18,19 @@
1818
1919function cardString(template, components) {
2020 var code;
21− code = 'AbstractButton { \
22− id: root; \
23− property var template; \
24− property var components; \
25− property var cardData; \
26− property real fontScale: 1.0; \
27− property int headerAlignment: Text.AlignLeft; \
28− property int fixedHeaderHeight: -1; \
29− property size fixedArtShapeSize: Qt.size(-1, -1); \
30− readonly property string title: cardData && cardData["title"] || ""; \
31− property bool asynchronous: true; \
32− property bool showHeader: true; \
33− implicitWidth: childrenRect.width; ';
21+ code = 'AbstractButton { \n\
22+ id: root; \n\
23+ property var template; \n\
24+ property var components; \n\
25+ property var cardData; \n\
26+ property real fontScale: 1.0; \n\
27+ property int headerAlignment: Text.AlignLeft; \n\
28+ property int fixedHeaderHeight: -1; \n\
29+ property size fixedArtShapeSize: Qt.size(-1, -1); \n\
30+ readonly property string title: cardData && cardData["title"] || ""; \n\
31+ property bool asynchronous: true; \n\
32+ property bool showHeader: true; \n\
33+ implicitWidth: childrenRect.width; \n';
3434
3535 var hasArt = components["art"] && components["art"]["field"] || false;
3636 var hasSummary = components["summary"] || false;
@@ -44,194 +44,194 @@ function cardString(template, components) {
4444 var hasHeaderRow = hasMascot && hasTitle;
4545
4646 if (hasBackground) {
47− code += 'Loader {\
48− id: backgroundLoader; \
49− objectName: "backgroundLoader"; \
50− anchors.fill: parent; \
51− asynchronous: root.asynchronous; \
52− visible: status == Loader.Ready; \
53− sourceComponent: UbuntuShape { \
54− objectName: "background"; \
55− radius: "medium"; \
56− color: getColor(0) || "white"; \
57− gradientColor: getColor(1) || color; \
58− anchors.fill: parent; \
59− image: backgroundImage.source ? backgroundImage : null; \
60− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \
61− property Image backgroundImage: Image { \
62− objectName: "backgroundImage"; \
63− source: { \
64− if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \
65− else if (template && typeof template["card-background"] === "string") return template["card-background"]; \
66− else return ""; \
67− } \
68− } \
69− function getColor(index) { \
70− if (cardData && typeof cardData["background"] === "object" \
71− && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \
72− return cardData["background"]["elements"][index]; \
73− } else if (template && typeof template["card-background"] === "object" \
74− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \
75− return template["card-background"]["elements"][index]; \
76− } else return undefined; \
77− } \
78− } \
79− }';
47+ code += 'Loader {\n\
48+ id: backgroundLoader; \n\
49+ objectName: "backgroundLoader"; \n\
50+ anchors.fill: parent; \n\
51+ asynchronous: root.asynchronous; \n\
52+ visible: status == Loader.Ready; \n\
53+ sourceComponent: UbuntuShape { \n\
54+ objectName: "background"; \n\
55+ radius: "medium"; \n\
56+ color: getColor(0) || "white"; \n\
57+ gradientColor: getColor(1) || color; \n\
58+ anchors.fill: parent; \n\
59+ image: backgroundImage.source ? backgroundImage : null; \n\
60+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \n\
61+ property Image backgroundImage: Image { \n\
62+ objectName: "backgroundImage"; \n\
63+ source: { \n\
64+ if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
65+ else if (template && typeof template["card-background"] === "string") return template["card-background"]; \n\
66+ else return ""; \n\
67+ } \n\
68+ } \n\
69+ function getColor(index) { \n\
70+ if (cardData && typeof cardData["background"] === "object" \n\
71+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
72+ return cardData["background"]["elements"][index]; \n\
73+ } else if (template && typeof template["card-background"] === "object" \n\
74+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \n\
75+ return template["card-background"]["elements"][index]; \n\
76+ } else return undefined; \n\
77+ } \n\
78+ } \n\
79+ }\n';
8080 }
8181
8282 if (hasArt) {
83− code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);'
83+ code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n'
8484 var imageWidthHeight;
8585 if (isHorizontal) {
8686 if (hasMascot || hasTitle) {
87− imageWidthHeight = 'width: height * artShape.aspect; \
88− height: headerHeight;'
87+ imageWidthHeight = 'width: height * artShape.aspect; \n\
88+ height: headerHeight;\n'
8989 } else {
9090 // This side of the else is a bit silly, who wants an horizontal layout without mascot and title?
9191 // So we define a "random" height of the image height + 2 gu for the margins
92− imageWidthHeight = 'width: height * artShape.aspect; \
92+ imageWidthHeight = 'width: height * artShape.aspect; \n\
9393 height: units.gu(7.625)';
9494 }
9595 } else {
96− imageWidthHeight = 'width: root.width; \
97− height: width / artShape.aspect;'
96+ imageWidthHeight = 'width: root.width; \n\
97+ height: width / artShape.aspect;\n'
9898 }
99− code += 'Item { \
100− id: artShapeHolder; \
101− height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; \
102− width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; \
103− ' + (isHorizontal ? 'anchors.left: parent.left;' : 'anchors.horizontalCenter: parent.horizontalCenter;' ) + '\
104− Loader { \
105− id: artShapeLoader; \
106− objectName: "artShapeLoader"; \
107− active: cardData && cardData["art"] || false; \
108− asynchronous: root.asynchronous; \
109− visible: status == Loader.Ready; \
110− sourceComponent: UbuntuShape { \
111− id: artShape; \
112− objectName: "artShape"; \
113− radius: "medium"; \
114− readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \
115− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \
116− Component.onCompleted: updateWidthHeightBindings(); \
117− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \
118− visible: image.status == Image.Ready; \
119− function updateWidthHeightBindings() { \
120− if (aspectSmallerThanImageAspect) { \
121− width = Qt.binding(function() { return !visible ? 0 : image.width }); \
122− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); \
123− } else { \
124− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); \
125− height = Qt.binding(function() { return !visible ? 0 : image.height }); \
126− } \
127− } \
128− image: Image { \
129− objectName: "artImage"; \
130− source: cardData && cardData["art"] || ""; \
131− cache: true; \
132− asynchronous: root.asynchronous; \
133− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; \
134− readonly property real aspect: implicitWidth / implicitHeight; \
135− ' + imageWidthHeight + '\
136− } \
137− } \
138− } \
139− }'
99+ code += 'Item { \n\
100+ id: artShapeHolder; \n\
101+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
102+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; \n\
103+ ' + (isHorizontal ? 'anchors.left: parent.left;' : 'anchors.horizontalCenter: parent.horizontalCenter;\n' ) + '\n\
104+ Loader { \n\
105+ id: artShapeLoader; \n\
106+ objectName: "artShapeLoader"; \n\
107+ active: cardData && cardData["art"] || false; \n\
108+ asynchronous: root.asynchronous; \n\
109+ visible: status == Loader.Ready; \n\
110+ sourceComponent: UbuntuShape { \n\
111+ id: artShape; \n\
112+ objectName: "artShape"; \n\
113+ radius: "medium"; \n\
114+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
115+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
116+ Component.onCompleted: updateWidthHeightBindings(); \n\
117+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
118+ visible: image.status == Image.Ready; \n\
119+ function updateWidthHeightBindings() { \n\
120+ if (aspectSmallerThanImageAspect) { \n\
121+ width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
122+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); \n\
123+ } else { \n\
124+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); \n\
125+ height = Qt.binding(function() { return !visible ? 0 : image.height }); \n\
126+ } \n\
127+ } \n\
128+ image: Image { \n\
129+ objectName: "artImage"; \n\
130+ source: cardData && cardData["art"] || ""; \n\
131+ cache: true; \n\
132+ asynchronous: root.asynchronous; \n\
133+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; \n\
134+ readonly property real aspect: implicitWidth / implicitHeight; \n\
135+ ' + imageWidthHeight + '\n\
136+ } \n\
137+ } \n\
138+ } \n\
139+ }\n'
140140 } else {
141− code += 'readonly property size artShapeSize: Qt.size(-1, -1);'
141+ code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
142142 }
143143
144144 if (inOverlay) {
145− var height = 'fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight;';
146− code += 'Loader { \
147− id: overlayLoader; \
148− anchors { \
149− left: artShapeHolder.left; \
150− right: artShapeHolder.right; \
151− bottom: artShapeHolder.bottom; \
152− } \
153− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \
154− asynchronous: root.asynchronous; \
155− visible: showHeader && status == Loader.Ready; \
156− sourceComponent: ShaderEffect { \
157− id: overlay; \
158− height: ' + height + ' \
159− opacity: 0.6; \
160− property var source: ShaderEffectSource { \
161− id: shaderSource; \
162− sourceItem: artShapeLoader.item; \
163− onVisibleChanged: if (visible) scheduleUpdate(); \
164− live: false; \
165− sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); \
166− } \
167− vertexShader: " \
168− uniform highp mat4 qt_Matrix; \
169− attribute highp vec4 qt_Vertex; \
170− attribute highp vec2 qt_MultiTexCoord0; \
171− varying highp vec2 coord; \
172− void main() { \
173− coord = qt_MultiTexCoord0; \
174− gl_Position = qt_Matrix * qt_Vertex; \
175− }"; \
176− fragmentShader: " \
177− varying highp vec2 coord; \
178− uniform sampler2D source; \
179− uniform lowp float qt_Opacity; \
180− void main() { \
181− lowp vec4 tex = texture2D(source, coord); \
182− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \
183− }"; \
184− } \
185− }';
145+ var height = 'fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight;\n';
146+ code += 'Loader { \n\
147+ id: overlayLoader; \n\
148+ anchors { \n\
149+ left: artShapeHolder.left; \n\
150+ right: artShapeHolder.right; \n\
151+ bottom: artShapeHolder.bottom; \n\
152+ } \n\
153+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
154+ asynchronous: root.asynchronous; \n\
155+ visible: showHeader && status == Loader.Ready; \n\
156+ sourceComponent: ShaderEffect { \n\
157+ id: overlay; \n\
158+ height: ' + height + ' \n\
159+ opacity: 0.6; \n\
160+ property var source: ShaderEffectSource { \n\
161+ id: shaderSource; \n\
162+ sourceItem: artShapeLoader.item; \n\
163+ onVisibleChanged: if (visible) scheduleUpdate(); \n\
164+ live: false; \n\
165+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); \n\
166+ } \n\
167+ vertexShader: " \n\
168+ uniform highp mat4 qt_Matrix; \n\
169+ attribute highp vec4 qt_Vertex; \n\
170+ attribute highp vec2 qt_MultiTexCoord0; \n\
171+ varying highp vec2 coord; \n\
172+ void main() { \n\
173+ coord = qt_MultiTexCoord0; \n\
174+ gl_Position = qt_Matrix * qt_Vertex; \n\
175+ }"; \n\
176+ fragmentShader: " \n\
177+ varying highp vec2 coord; \n\
178+ uniform sampler2D source; \n\
179+ uniform lowp float qt_Opacity; \n\
180+ void main() { \n\
181+ lowp vec4 tex = texture2D(source, coord); \n\
182+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \n\
183+ }"; \n\
184+ } \n\
185+ }\n';
186186 }
187187
188188 var headerVerticalAnchors;
189189 if (inOverlay) {
190− headerVerticalAnchors = 'anchors.bottom: artShapeHolder.bottom; \
191− anchors.bottomMargin: units.gu(1);';
190+ headerVerticalAnchors = 'anchors.bottom: artShapeHolder.bottom; \n\
191+ anchors.bottomMargin: units.gu(1);\n';
192192 } else {
193193 if (hasArt) {
194194 if (isHorizontal) {
195− headerVerticalAnchors = 'anchors.top: artShapeHolder.top; \
196− anchors.topMargin: units.gu(1);';
195+ headerVerticalAnchors = 'anchors.top: artShapeHolder.top; \n\
196+ anchors.topMargin: units.gu(1);\n';
197197 } else {
198− headerVerticalAnchors = 'anchors.top: artShapeHolder.bottom; \
199− anchors.topMargin: units.gu(1);';
198+ headerVerticalAnchors = 'anchors.top: artShapeHolder.bottom; \n\
199+ anchors.topMargin: units.gu(1);\n';
200200 }
201201 } else {
202− headerVerticalAnchors = 'anchors.top: parent.top; \
203− anchors.topMargin: units.gu(1);';
202+ headerVerticalAnchors = 'anchors.top: parent.top; \n\
203+ anchors.topMargin: units.gu(1);\n';
204204 }
205205 }
206206 var headerLeftAnchor;
207207 var headerLeftAnchorHasMagin = false;
208208 if (isHorizontal && hasArt) {
209− headerLeftAnchor = 'anchors.left: artShapeHolder.right; \
210− anchors.leftMargin: units.gu(1);';
209+ headerLeftAnchor = 'anchors.left: artShapeHolder.right; \n\
210+ anchors.leftMargin: units.gu(1);\n';
211211 headerLeftAnchorHasMagin = true;
212212 } else {
213− headerLeftAnchor = 'anchors.left: parent.left;';
213+ headerLeftAnchor = 'anchors.left: parent.left;\n';
214214 }
215215
216216 if (hasHeaderRow) {
217− code += 'readonly property int headerHeight: row.height + row.margins * 2;'
218− code += 'Row { \
219− id: row; \
220− objectName: "outerRow"; \
221− property real margins: units.gu(1); \
222− spacing: margins; \
223− ' + headerVerticalAnchors + '\
224− ' + headerLeftAnchor + '\
225− anchors.right: parent.right; \
226− anchors.margins: margins;';
217+ code += 'readonly property int headerHeight: row.height + row.margins * 2;\n'
218+ code += 'Row { \n\
219+ id: row; \n\
220+ objectName: "outerRow"; \n\
221+ property real margins: units.gu(1); \n\
222+ spacing: margins; \n\
223+ ' + headerVerticalAnchors + '\n\
224+ ' + headerLeftAnchor + '\n\
225+ anchors.right: parent.right; \n\
226+ anchors.margins: margins;\n';
227227 } else if (hasMascot) {
228− code += 'readonly property int headerHeight: mascotImage.height + units.gu(1) * 2;'
228+ code += 'readonly property int headerHeight: mascotImage.height + units.gu(1) * 2;\n'
229229 } else if (hasSubtitle) {
230− code += 'readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;'
230+ code += 'readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;\n'
231231 } else if (hasTitle) {
232− code += 'readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;'
232+ code += 'readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;\n'
233233 } else {
234− code += 'readonly property int headerHeight: 0;'
234+ code += 'readonly property int headerHeight: 0;\n'
235235 }
236236
237237 if (hasMascot) {
@@ -241,40 +241,40 @@ function cardString(template, components) {
241241 anchors += headerLeftAnchor;
242242 anchors += headerVerticalAnchors;
243243 if (!headerLeftAnchorHasMagin) {
244− anchors += 'anchors.leftMargin: units.gu(1);'
244+ anchors += 'anchors.leftMargin: units.gu(1);\n'
245245 }
246246 } else {
247247 anchors = "anchors.verticalCenter: parent.verticalCenter;"
248248 }
249249
250250 if (useMascotShape) {
251− code += 'Loader { \
252− id: mascotShapeLoader; \
253− objectName: "mascotShapeLoader"; \
254− asynchronous: root.asynchronous; \
255− active: mascotImage.status === Image.Ready; \
256− visible: showHeader && active && status == Loader.Ready; \
257− width: units.gu(6); \
258− height: units.gu(5.625); \
259− sourceComponent: UbuntuShape { image: mascotImage } \
260− ' + anchors + '\
261− }';
251+ code += 'Loader { \n\
252+ id: mascotShapeLoader; \n\
253+ objectName: "mascotShapeLoader"; \n\
254+ asynchronous: root.asynchronous; \n\
255+ active: mascotImage.status === Image.Ready; \n\
256+ visible: showHeader && active && status == Loader.Ready; \n\
257+ width: units.gu(6); \n\
258+ height: units.gu(5.625); \n\
259+ sourceComponent: UbuntuShape { image: mascotImage } \n\
260+ ' + anchors + '\n\
261+ }\n';
262262 }
263263
264− code += 'Image { \
265− id: mascotImage; \
266− objectName: "mascotImage"; \
267− ' + anchors + '\
268− readonly property int maxSize: Math.max(width, height) * 4; \
269− source: cardData && cardData["mascot"]; \
270− width: units.gu(6); \
271− height: units.gu(5.625); \
272− sourceSize { width: maxSize; height: maxSize } \
273− fillMode: Image.PreserveAspectCrop; \
274− horizontalAlignment: Image.AlignHCenter; \
275− verticalAlignment: Image.AlignVCenter; \
276− visible: showHeader && ' + (useMascotShape ? 'false' : 'true') + '; \
277− }';
264+ code += 'Image { \n\
265+ id: mascotImage; \n\
266+ objectName: "mascotImage"; \n\
267+ ' + anchors + '\n\
268+ readonly property int maxSize: Math.max(width, height) * 4; \n\
269+ source: cardData && cardData["mascot"]; \n\
270+ width: units.gu(6); \n\
271+ height: units.gu(5.625); \n\
272+ sourceSize { width: maxSize; height: maxSize } \n\
273+ fillMode: Image.PreserveAspectCrop; \n\
274+ horizontalAlignment: Image.AlignHCenter; \n\
275+ verticalAlignment: Image.AlignVCenter; \n\
276+ visible: showHeader && ' + (useMascotShape ? 'false' : 'true') + '; \n\
277+ }\n';
278278 }
279279
280280 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"';
@@ -294,81 +294,81 @@ function cardString(template, components) {
294294 var titleAnchors = "";
295295 var subtitleAnchors = "";
296296 if (hasMascot && hasSubtitle) {
297− titleAnchors = 'anchors { left: parent.left; right: parent.right }';
297+ titleAnchors = 'anchors { left: parent.left; right: parent.right }\n';
298298 subtitleAnchors = titleAnchors;
299− code += 'Column { \
300− anchors.verticalCenter: parent.verticalCenter; \
301− spacing: units.dp(2); \
302− width: parent.width - x;';
299+ code += 'Column { \n\
300+ anchors.verticalCenter: parent.verticalCenter; \n\
301+ spacing: units.dp(2); \n\
302+ width: parent.width - x;\n';
303303 } else if (hasMascot) {
304− titleAnchors = 'anchors.verticalCenter: parent.verticalCenter;'
304+ titleAnchors = 'anchors.verticalCenter: parent.verticalCenter;\n'
305305 } else if (inOverlay) {
306− titleAnchors = 'anchors.left: parent.left; \
307− anchors.leftMargin: units.gu(1); \
308− anchors.right: parent.right; \
309− anchors.top: overlayLoader.top; \
310− anchors.topMargin: units.gu(1);';
311− subtitleAnchors = 'anchors.left: titleLabel.left; \
312− anchors.leftMargin: titleLabel.leftMargin; \
313− anchors.right: titleLabel.right; \
314− anchors.top: titleLabel.bottom; \
315− anchors.topMargin: units.dp(2);';
306+ titleAnchors = 'anchors.left: parent.left; \n\
307+ anchors.leftMargin: units.gu(1); \n\
308+ anchors.right: parent.right; \n\
309+ anchors.top: overlayLoader.top; \n\
310+ anchors.topMargin: units.gu(1);\n';
311+ subtitleAnchors = 'anchors.left: titleLabel.left; \n\
312+ anchors.leftMargin: titleLabel.leftMargin; \n\
313+ anchors.right: titleLabel.right; \n\
314+ anchors.top: titleLabel.bottom; \n\
315+ anchors.topMargin: units.dp(2);\n';
316316 } else {
317317 titleAnchors = "anchors.right: parent.right;";
318318 if (hasMascot) {
319− titleAnchors += 'anchors.left: mascotImage.right; \
320− anchors.leftMargin: units.gu(1);';
319+ titleAnchors += 'anchors.left: mascotImage.right; \n\
320+ anchors.leftMargin: units.gu(1);\n';
321321 } else {
322322 titleAnchors += headerLeftAnchor;
323323 }
324324 titleAnchors += headerVerticalAnchors;
325− subtitleAnchors = 'anchors.left: titleLabel.left; \
326− anchors.leftMargin: titleLabel.leftMargin; \
327− anchors.right: titleLabel.right; \
328− anchors.top: titleLabel.bottom; \
329− anchors.topMargin: units.dp(2);';
325+ subtitleAnchors = 'anchors.left: titleLabel.left; \n\
326+ anchors.leftMargin: titleLabel.leftMargin; \n\
327+ anchors.right: titleLabel.right; \n\
328+ anchors.top: titleLabel.bottom; \n\
329+ anchors.topMargin: units.dp(2);\n';
330330 }
331331
332− code += 'Label { \
333− id: titleLabel; \
334− objectName: "titleLabel"; \
335− ' + titleAnchors + '\
336− elide: Text.ElideRight; \
337− fontSize: "small"; \
338− wrapMode: Text.Wrap; \
339− maximumLineCount: 2; \
340− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \
341− color: ' + color + '; \
342− visible: showHeader ' + (inOverlay ? '&& overlayLoader.active': '') + '; \
343− text: root.title; \
344− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \
345− horizontalAlignment: root.headerAlignment; \
346− }';
332+ code += 'Label { \n\
333+ id: titleLabel; \n\
334+ objectName: "titleLabel"; \n\
335+ ' + titleAnchors + '\n\
336+ elide: Text.ElideRight; \n\
337+ fontSize: "small"; \n\
338+ wrapMode: Text.Wrap; \n\
339+ maximumLineCount: 2; \n\
340+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
341+ color: ' + color + '; \n\
342+ visible: showHeader ' + (inOverlay ? '&& overlayLoader.active': '') + '; \n\
343+ text: root.title; \n\
344+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \n\
345+ horizontalAlignment: root.headerAlignment; \n\
346+ }\n';
347347
348348 if (hasSubtitle) {
349− code += 'Label { \
350− id: subtitleLabel; \
351− objectName: "subtitleLabel"; \
352− ' + subtitleAnchors + '\
353− elide: Text.ElideRight; \
354− fontSize: "small"; \
355− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \
356− color: ' + color + '; \
357− visible: titleLabel.visible && titleLabel.text; \
358− text: cardData && cardData["subtitle"] || ""; \
359− font.weight: Font.Light; \
360− horizontalAlignment: root.headerAlignment; \
361− }';
349+ code += 'Label { \n\
350+ id: subtitleLabel; \n\
351+ objectName: "subtitleLabel"; \n\
352+ ' + subtitleAnchors + '\n\
353+ elide: Text.ElideRight; \n\
354+ fontSize: "small"; \n\
355+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
356+ color: ' + color + '; \n\
357+ visible: titleLabel.visible && titleLabel.text; \n\
358+ text: cardData && cardData["subtitle"] || ""; \n\
359+ font.weight: Font.Light; \n\
360+ horizontalAlignment: root.headerAlignment; \n\
361+ }\n';
362362
363363 // Close Column
364364 if (hasMascot)
365− code += '}';
365+ code += '}\n';
366366 }
367367 }
368368
369369 if (hasHeaderRow) {
370370 // Close Row
371− code += '}';
371+ code += '}\n';
372372 }
373373
374374 if (hasSummary) {
@@ -387,48 +387,48 @@ function cardString(template, components) {
387387 } else {
388388 color = '"grey"';
389389 }
390− code += 'Label { \
391− id: summary; \
392− objectName: "summaryLabel"; \
393− anchors { \
394− top: ' + summaryTopAnchor + '; \
395− left: parent.left; \
396− right: parent.right; \
397− margins: units.gu(1); \
398− topMargin: ' + (hasMascot || hasSubtitle ? 'anchors.margins' : 0) + '; \
399− } \
400− wrapMode: Text.Wrap; \
401− maximumLineCount: 5; \
402− elide: Text.ElideRight; \
403− text: cardData && cardData["summary"] || ""; \
404− height: text ? implicitHeight : 0; \
405− fontSize: "small"; \
406− color: ' + color + '; \
407− }';
390+ code += 'Label { \n\
391+ id: summary; \n\
392+ objectName: "summaryLabel"; \n\
393+ anchors { \n\
394+ top: ' + summaryTopAnchor + '; \n\
395+ left: parent.left; \n\
396+ right: parent.right; \n\
397+ margins: units.gu(1); \n\
398+ topMargin: ' + (hasMascot || hasSubtitle ? 'anchors.margins' : 0) + '; \n\
399+ } \n\
400+ wrapMode: Text.Wrap; \n\
401+ maximumLineCount: 5; \n\
402+ elide: Text.ElideRight; \n\
403+ text: cardData && cardData["summary"] || ""; \n\
404+ height: text ? implicitHeight : 0; \n\
405+ fontSize: "small"; \n\
406+ color: ' + color + '; \n\
407+ }\n';
408408 }
409409
410410 // Close the AbstractButton
411411 if (hasSummary) {
412− code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);';
412+ code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
413413 } else if (hasHeaderRow) {
414− code += 'implicitHeight: row.y + row.height + units.gu(1);';
414+ code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
415415 } else if (hasMascot) {
416− code += 'implicitHeight: mascotImage.y + mascotImage.height;';
416+ code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
417417 } else if (hasSubtitle) {
418− code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);';
418+ code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
419419 } else if (hasTitle) {
420− code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);';
420+ code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);\n';
421421 }
422− code += '}';
422+ code += '}\n';
423423
424424 return code;
425425}
426426
427427function createCardComponent(parent, template, components) {
428− var imports = 'import QtQuick 2.2; \
429− import Ubuntu.Components 0.1; \
430− import Ubuntu.Thumbnailer 0.1;';
428+ var imports = 'import QtQuick 2.2; \n\
429+ import Ubuntu.Components 0.1; \n\
430+ import Ubuntu.Thumbnailer 0.1;\n';
431431 var card = cardString(template, components);
432− var code = imports + 'Component {' + card + '}';
432+ var code = imports + 'Component {\n' + card + '}\n';
433433 return Qt.createQmlObject(code, parent, "createCardComponent");
434434}
tests/plugins/Dash/cardcreator/1.res+79−1
@@ -1 +1,79 @@
1−AbstractButton { id: root; property var template; property var components; property var cardData; property real fontScale: 1.0; property int headerAlignment: Text.AlignLeft; property int fixedHeaderHeight: -1; property size fixedArtShapeSize: Qt.size(-1, -1); readonly property string title: cardData && cardData["title"] || ""; property bool asynchronous: true; property bool showHeader: true; implicitWidth: childrenRect.width; readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);Item { id: artShapeHolder; height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; anchors.horizontalCenter: parent.horizontalCenter; Loader { id: artShapeLoader; objectName: "artShapeLoader"; active: cardData && cardData["art"] || false; asynchronous: root.asynchronous; visible: status == Loader.Ready; sourceComponent: UbuntuShape { id: artShape; objectName: "artShape"; radius: "medium"; readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; Component.onCompleted: updateWidthHeightBindings(); onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); visible: image.status == Image.Ready; function updateWidthHeightBindings() { if (aspectSmallerThanImageAspect) { width = Qt.binding(function() { return !visible ? 0 : image.width }); height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); } else { width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); height = Qt.binding(function() { return !visible ? 0 : image.height }); } } image: Image { objectName: "artImage"; source: cardData && cardData["art"] || ""; cache: true; asynchronous: root.asynchronous; fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; readonly property real aspect: implicitWidth / implicitHeight; width: root.width; height: width / artShape.aspect; } } } }readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;Label { id: titleLabel; objectName: "titleLabel"; anchors.right: parent.right;anchors.left: parent.left;anchors.top: artShapeHolder.bottom; anchors.topMargin: units.gu(1); elide: Text.ElideRight; fontSize: "small"; wrapMode: Text.Wrap; maximumLineCount: 2; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "grey"; visible: showHeader ; text: root.title; font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; horizontalAlignment: root.headerAlignment; }implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);}
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property real fontScale: 1.0;
7+ property int headerAlignment: Text.AlignLeft;
8+ property int fixedHeaderHeight: -1;
9+ property size fixedArtShapeSize: Qt.size(-1, -1);
10+ readonly property string title: cardData && cardData["title"] || "";
11+ property bool asynchronous: true;
12+ property bool showHeader: true;
13+ implicitWidth: childrenRect.width;
14+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15+Item {
16+ id: artShapeHolder;
17+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
18+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
19+ anchors.horizontalCenter: parent.horizontalCenter;
20+
21+ Loader {
22+ id: artShapeLoader;
23+ objectName: "artShapeLoader";
24+ active: cardData && cardData["art"] || false;
25+ asynchronous: root.asynchronous;
26+ visible: status == Loader.Ready;
27+ sourceComponent: UbuntuShape {
28+ id: artShape;
29+ objectName: "artShape";
30+ radius: "medium";
31+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33+ Component.onCompleted: updateWidthHeightBindings();
34+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35+ visible: image.status == Image.Ready;
36+ function updateWidthHeightBindings() {
37+ if (aspectSmallerThanImageAspect) {
38+ width = Qt.binding(function() { return !visible ? 0 : image.width });
39+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
40+ } else {
41+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
42+ height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ }
44+ }
45+ image: Image {
46+ objectName: "artImage";
47+ source: cardData && cardData["art"] || "";
48+ cache: true;
49+ asynchronous: root.asynchronous;
50+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
51+ readonly property real aspect: implicitWidth / implicitHeight;
52+ width: root.width;
53+ height: width / artShape.aspect;
54+
55+ }
56+ }
57+ }
58+ }
59+readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin * 2;
60+Label {
61+ id: titleLabel;
62+ objectName: "titleLabel";
63+ anchors.right: parent.right;anchors.left: parent.left;
64+anchors.top: artShapeHolder.bottom;
65+ anchors.topMargin: units.gu(1);
66+
67+ elide: Text.ElideRight;
68+ fontSize: "small";
69+ wrapMode: Text.Wrap;
70+ maximumLineCount: 2;
71+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
72+ color: "grey";
73+ visible: showHeader ;
74+ text: root.title;
75+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76+ horizontalAlignment: root.headerAlignment;
77+ }
78+implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
79+}
tests/plugins/Dash/cardcreator/2.res+112−1
@@ -1 +1,112 @@
1−AbstractButton { id: root; property var template; property var components; property var cardData; property real fontScale: 1.0; property int headerAlignment: Text.AlignLeft; property int fixedHeaderHeight: -1; property size fixedArtShapeSize: Qt.size(-1, -1); readonly property string title: cardData && cardData["title"] || ""; property bool asynchronous: true; property bool showHeader: true; implicitWidth: childrenRect.width; Loader { id: backgroundLoader; objectName: "backgroundLoader"; anchors.fill: parent; asynchronous: root.asynchronous; visible: status == Loader.Ready; sourceComponent: UbuntuShape { objectName: "background"; radius: "medium"; color: getColor(0) || "white"; gradientColor: getColor(1) || color; anchors.fill: parent; image: backgroundImage.source ? backgroundImage : null; property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; property Image backgroundImage: Image { objectName: "backgroundImage"; source: { if (cardData && typeof cardData["background"] === "string") return cardData["background"]; else if (template && typeof template["card-background"] === "string") return template["card-background"]; else return ""; } } function getColor(index) { if (cardData && typeof cardData["background"] === "object" && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { return cardData["background"]["elements"][index]; } else if (template && typeof template["card-background"] === "object" && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { return template["card-background"]["elements"][index]; } else return undefined; } } }readonly property size artShapeSize: Qt.size(-1, -1);readonly property int headerHeight: row.height + row.margins * 2;Row { id: row; objectName: "outerRow"; property real margins: units.gu(1); spacing: margins; anchors.top: parent.top; anchors.topMargin: units.gu(1); anchors.left: parent.left; anchors.right: parent.right; anchors.margins: margins;Image { id: mascotImage; objectName: "mascotImage"; anchors.verticalCenter: parent.verticalCenter; readonly property int maxSize: Math.max(width, height) * 4; source: cardData && cardData["mascot"]; width: units.gu(6); height: units.gu(5.625); sourceSize { width: maxSize; height: maxSize } fillMode: Image.PreserveAspectCrop; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignVCenter; visible: showHeader && true; }Column { anchors.verticalCenter: parent.verticalCenter; spacing: units.dp(2); width: parent.width - x;Label { id: titleLabel; objectName: "titleLabel"; anchors { left: parent.left; right: parent.right } elide: Text.ElideRight; fontSize: "small"; wrapMode: Text.Wrap; maximumLineCount: 2; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"; visible: showHeader ; text: root.title; font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; horizontalAlignment: root.headerAlignment; }Label { id: subtitleLabel; objectName: "subtitleLabel"; anchors { left: parent.left; right: parent.right } elide: Text.ElideRight; fontSize: "small"; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"; visible: titleLabel.visible && titleLabel.text; text: cardData && cardData["subtitle"] || ""; font.weight: Font.Light; horizontalAlignment: root.headerAlignment; }}}implicitHeight: row.y + row.height + units.gu(1);}
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property real fontScale: 1.0;
7+ property int headerAlignment: Text.AlignLeft;
8+ property int fixedHeaderHeight: -1;
9+ property size fixedArtShapeSize: Qt.size(-1, -1);
10+ readonly property string title: cardData && cardData["title"] || "";
11+ property bool asynchronous: true;
12+ property bool showHeader: true;
13+ implicitWidth: childrenRect.width;
14+Loader {
15+ id: backgroundLoader;
16+ objectName: "backgroundLoader";
17+ anchors.fill: parent;
18+ asynchronous: root.asynchronous;
19+ visible: status == Loader.Ready;
20+ sourceComponent: UbuntuShape {
21+ objectName: "background";
22+ radius: "medium";
23+ color: getColor(0) || "white";
24+ gradientColor: getColor(1) || color;
25+ anchors.fill: parent;
26+ image: backgroundImage.source ? backgroundImage : null;
27+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
28+ property Image backgroundImage: Image {
29+ objectName: "backgroundImage";
30+ source: {
31+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
32+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
33+ else return "";
34+ }
35+ }
36+ function getColor(index) {
37+ if (cardData && typeof cardData["background"] === "object"
38+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
39+ return cardData["background"]["elements"][index];
40+ } else if (template && typeof template["card-background"] === "object"
41+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
42+ return template["card-background"]["elements"][index];
43+ } else return undefined;
44+ }
45+ }
46+ }
47+readonly property size artShapeSize: Qt.size(-1, -1);
48+readonly property int headerHeight: row.height + row.margins * 2;
49+Row {
50+ id: row;
51+ objectName: "outerRow";
52+ property real margins: units.gu(1);
53+ spacing: margins;
54+ anchors.top: parent.top;
55+ anchors.topMargin: units.gu(1);
56+
57+ anchors.left: parent.left;
58+
59+ anchors.right: parent.right;
60+ anchors.margins: margins;
61+Image {
62+ id: mascotImage;
63+ objectName: "mascotImage";
64+ anchors.verticalCenter: parent.verticalCenter;
65+ readonly property int maxSize: Math.max(width, height) * 4;
66+ source: cardData && cardData["mascot"];
67+ width: units.gu(6);
68+ height: units.gu(5.625);
69+ sourceSize { width: maxSize; height: maxSize }
70+ fillMode: Image.PreserveAspectCrop;
71+ horizontalAlignment: Image.AlignHCenter;
72+ verticalAlignment: Image.AlignVCenter;
73+ visible: showHeader && true;
74+ }
75+Column {
76+ anchors.verticalCenter: parent.verticalCenter;
77+ spacing: units.dp(2);
78+ width: parent.width - x;
79+Label {
80+ id: titleLabel;
81+ objectName: "titleLabel";
82+ anchors { left: parent.left; right: parent.right }
83+
84+ elide: Text.ElideRight;
85+ fontSize: "small";
86+ wrapMode: Text.Wrap;
87+ maximumLineCount: 2;
88+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
89+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
90+ visible: showHeader ;
91+ text: root.title;
92+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
93+ horizontalAlignment: root.headerAlignment;
94+ }
95+Label {
96+ id: subtitleLabel;
97+ objectName: "subtitleLabel";
98+ anchors { left: parent.left; right: parent.right }
99+
100+ elide: Text.ElideRight;
101+ fontSize: "small";
102+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
103+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey";
104+ visible: titleLabel.visible && titleLabel.text;
105+ text: cardData && cardData["subtitle"] || "";
106+ font.weight: Font.Light;
107+ horizontalAlignment: root.headerAlignment;
108+ }
109+}
110+}
111+implicitHeight: row.y + row.height + units.gu(1);
112+}
tests/plugins/Dash/cardcreator/3.res+97−1
@@ -1 +1,97 @@
1−AbstractButton { id: root; property var template; property var components; property var cardData; property real fontScale: 1.0; property int headerAlignment: Text.AlignLeft; property int fixedHeaderHeight: -1; property size fixedArtShapeSize: Qt.size(-1, -1); readonly property string title: cardData && cardData["title"] || ""; property bool asynchronous: true; property bool showHeader: true; implicitWidth: childrenRect.width; readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);Item { id: artShapeHolder; height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; anchors.horizontalCenter: parent.horizontalCenter; Loader { id: artShapeLoader; objectName: "artShapeLoader"; active: cardData && cardData["art"] || false; asynchronous: root.asynchronous; visible: status == Loader.Ready; sourceComponent: UbuntuShape { id: artShape; objectName: "artShape"; radius: "medium"; readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; Component.onCompleted: updateWidthHeightBindings(); onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); visible: image.status == Image.Ready; function updateWidthHeightBindings() { if (aspectSmallerThanImageAspect) { width = Qt.binding(function() { return !visible ? 0 : image.width }); height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); } else { width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); height = Qt.binding(function() { return !visible ? 0 : image.height }); } } image: Image { objectName: "artImage"; source: cardData && cardData["art"] || ""; cache: true; asynchronous: root.asynchronous; fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; readonly property real aspect: implicitWidth / implicitHeight; width: root.width; height: width / artShape.aspect; } } } }readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;Label { id: titleLabel; objectName: "titleLabel"; anchors.right: parent.right;anchors.left: parent.left;anchors.top: artShapeHolder.bottom; anchors.topMargin: units.gu(1); elide: Text.ElideRight; fontSize: "small"; wrapMode: Text.Wrap; maximumLineCount: 2; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "grey"; visible: showHeader ; text: root.title; font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; horizontalAlignment: root.headerAlignment; }Label { id: subtitleLabel; objectName: "subtitleLabel"; anchors.left: titleLabel.left; anchors.leftMargin: titleLabel.leftMargin; anchors.right: titleLabel.right; anchors.top: titleLabel.bottom; anchors.topMargin: units.dp(2); elide: Text.ElideRight; fontSize: "small"; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "grey"; visible: titleLabel.visible && titleLabel.text; text: cardData && cardData["subtitle"] || ""; font.weight: Font.Light; horizontalAlignment: root.headerAlignment; }implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);}
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property real fontScale: 1.0;
7+ property int headerAlignment: Text.AlignLeft;
8+ property int fixedHeaderHeight: -1;
9+ property size fixedArtShapeSize: Qt.size(-1, -1);
10+ readonly property string title: cardData && cardData["title"] || "";
11+ property bool asynchronous: true;
12+ property bool showHeader: true;
13+ implicitWidth: childrenRect.width;
14+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15+Item {
16+ id: artShapeHolder;
17+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
18+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
19+ anchors.horizontalCenter: parent.horizontalCenter;
20+
21+ Loader {
22+ id: artShapeLoader;
23+ objectName: "artShapeLoader";
24+ active: cardData && cardData["art"] || false;
25+ asynchronous: root.asynchronous;
26+ visible: status == Loader.Ready;
27+ sourceComponent: UbuntuShape {
28+ id: artShape;
29+ objectName: "artShape";
30+ radius: "medium";
31+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33+ Component.onCompleted: updateWidthHeightBindings();
34+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35+ visible: image.status == Image.Ready;
36+ function updateWidthHeightBindings() {
37+ if (aspectSmallerThanImageAspect) {
38+ width = Qt.binding(function() { return !visible ? 0 : image.width });
39+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
40+ } else {
41+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
42+ height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ }
44+ }
45+ image: Image {
46+ objectName: "artImage";
47+ source: cardData && cardData["art"] || "";
48+ cache: true;
49+ asynchronous: root.asynchronous;
50+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
51+ readonly property real aspect: implicitWidth / implicitHeight;
52+ width: root.width;
53+ height: width / artShape.aspect;
54+
55+ }
56+ }
57+ }
58+ }
59+readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;
60+Label {
61+ id: titleLabel;
62+ objectName: "titleLabel";
63+ anchors.right: parent.right;anchors.left: parent.left;
64+anchors.top: artShapeHolder.bottom;
65+ anchors.topMargin: units.gu(1);
66+
67+ elide: Text.ElideRight;
68+ fontSize: "small";
69+ wrapMode: Text.Wrap;
70+ maximumLineCount: 2;
71+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
72+ color: "grey";
73+ visible: showHeader ;
74+ text: root.title;
75+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
76+ horizontalAlignment: root.headerAlignment;
77+ }
78+Label {
79+ id: subtitleLabel;
80+ objectName: "subtitleLabel";
81+ anchors.left: titleLabel.left;
82+ anchors.leftMargin: titleLabel.leftMargin;
83+ anchors.right: titleLabel.right;
84+ anchors.top: titleLabel.bottom;
85+ anchors.topMargin: units.dp(2);
86+
87+ elide: Text.ElideRight;
88+ fontSize: "small";
89+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
90+ color: "grey";
91+ visible: titleLabel.visible && titleLabel.text;
92+ text: cardData && cardData["subtitle"] || "";
93+ font.weight: Font.Light;
94+ horizontalAlignment: root.headerAlignment;
95+ }
96+implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
97+}
tests/plugins/Dash/cardcreator/4.res+90−1
@@ -1 +1,90 @@
1−AbstractButton { id: root; property var template; property var components; property var cardData; property real fontScale: 1.0; property int headerAlignment: Text.AlignLeft; property int fixedHeaderHeight: -1; property size fixedArtShapeSize: Qt.size(-1, -1); readonly property string title: cardData && cardData["title"] || ""; property bool asynchronous: true; property bool showHeader: true; implicitWidth: childrenRect.width; readonly property size artShapeSize: Qt.size(-1, -1);readonly property int headerHeight: row.height + row.margins * 2;Row { id: row; objectName: "outerRow"; property real margins: units.gu(1); spacing: margins; anchors.top: parent.top; anchors.topMargin: units.gu(1); anchors.left: parent.left; anchors.right: parent.right; anchors.margins: margins;Loader { id: mascotShapeLoader; objectName: "mascotShapeLoader"; asynchronous: root.asynchronous; active: mascotImage.status === Image.Ready; visible: showHeader && active && status == Loader.Ready; width: units.gu(6); height: units.gu(5.625); sourceComponent: UbuntuShape { image: mascotImage } anchors.verticalCenter: parent.verticalCenter; }Image { id: mascotImage; objectName: "mascotImage"; anchors.verticalCenter: parent.verticalCenter; readonly property int maxSize: Math.max(width, height) * 4; source: cardData && cardData["mascot"]; width: units.gu(6); height: units.gu(5.625); sourceSize { width: maxSize; height: maxSize } fillMode: Image.PreserveAspectCrop; horizontalAlignment: Image.AlignHCenter; verticalAlignment: Image.AlignVCenter; visible: showHeader && false; }Column { anchors.verticalCenter: parent.verticalCenter; spacing: units.dp(2); width: parent.width - x;Label { id: titleLabel; objectName: "titleLabel"; anchors { left: parent.left; right: parent.right } elide: Text.ElideRight; fontSize: "small"; wrapMode: Text.Wrap; maximumLineCount: 2; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "grey"; visible: showHeader ; text: root.title; font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; horizontalAlignment: root.headerAlignment; }Label { id: subtitleLabel; objectName: "subtitleLabel"; anchors { left: parent.left; right: parent.right } elide: Text.ElideRight; fontSize: "small"; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "grey"; visible: titleLabel.visible && titleLabel.text; text: cardData && cardData["subtitle"] || ""; font.weight: Font.Light; horizontalAlignment: root.headerAlignment; }}}implicitHeight: row.y + row.height + units.gu(1);}
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property real fontScale: 1.0;
7+ property int headerAlignment: Text.AlignLeft;
8+ property int fixedHeaderHeight: -1;
9+ property size fixedArtShapeSize: Qt.size(-1, -1);
10+ readonly property string title: cardData && cardData["title"] || "";
11+ property bool asynchronous: true;
12+ property bool showHeader: true;
13+ implicitWidth: childrenRect.width;
14+readonly property size artShapeSize: Qt.size(-1, -1);
15+readonly property int headerHeight: row.height + row.margins * 2;
16+Row {
17+ id: row;
18+ objectName: "outerRow";
19+ property real margins: units.gu(1);
20+ spacing: margins;
21+ anchors.top: parent.top;
22+ anchors.topMargin: units.gu(1);
23+
24+ anchors.left: parent.left;
25+
26+ anchors.right: parent.right;
27+ anchors.margins: margins;
28+Loader {
29+ id: mascotShapeLoader;
30+ objectName: "mascotShapeLoader";
31+ asynchronous: root.asynchronous;
32+ active: mascotImage.status === Image.Ready;
33+ visible: showHeader && active && status == Loader.Ready;
34+ width: units.gu(6);
35+ height: units.gu(5.625);
36+ sourceComponent: UbuntuShape { image: mascotImage }
37+ anchors.verticalCenter: parent.verticalCenter;
38+ }
39+Image {
40+ id: mascotImage;
41+ objectName: "mascotImage";
42+ anchors.verticalCenter: parent.verticalCenter;
43+ readonly property int maxSize: Math.max(width, height) * 4;
44+ source: cardData && cardData["mascot"];
45+ width: units.gu(6);
46+ height: units.gu(5.625);
47+ sourceSize { width: maxSize; height: maxSize }
48+ fillMode: Image.PreserveAspectCrop;
49+ horizontalAlignment: Image.AlignHCenter;
50+ verticalAlignment: Image.AlignVCenter;
51+ visible: showHeader && false;
52+ }
53+Column {
54+ anchors.verticalCenter: parent.verticalCenter;
55+ spacing: units.dp(2);
56+ width: parent.width - x;
57+Label {
58+ id: titleLabel;
59+ objectName: "titleLabel";
60+ anchors { left: parent.left; right: parent.right }
61+
62+ elide: Text.ElideRight;
63+ fontSize: "small";
64+ wrapMode: Text.Wrap;
65+ maximumLineCount: 2;
66+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
67+ color: "grey";
68+ visible: showHeader ;
69+ text: root.title;
70+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
71+ horizontalAlignment: root.headerAlignment;
72+ }
73+Label {
74+ id: subtitleLabel;
75+ objectName: "subtitleLabel";
76+ anchors { left: parent.left; right: parent.right }
77+
78+ elide: Text.ElideRight;
79+ fontSize: "small";
80+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
81+ color: "grey";
82+ visible: titleLabel.visible && titleLabel.text;
83+ text: cardData && cardData["subtitle"] || "";
84+ font.weight: Font.Light;
85+ horizontalAlignment: root.headerAlignment;
86+ }
87+}
88+}
89+implicitHeight: row.y + row.height + units.gu(1);
90+}
tests/plugins/Dash/cardcreator/5.res+140−1
@@ -1 +1,140 @@
1−AbstractButton { id: root; property var template; property var components; property var cardData; property real fontScale: 1.0; property int headerAlignment: Text.AlignLeft; property int fixedHeaderHeight: -1; property size fixedArtShapeSize: Qt.size(-1, -1); readonly property string title: cardData && cardData["title"] || ""; property bool asynchronous: true; property bool showHeader: true; implicitWidth: childrenRect.width; readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);Item { id: artShapeHolder; height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; anchors.horizontalCenter: parent.horizontalCenter; Loader { id: artShapeLoader; objectName: "artShapeLoader"; active: cardData && cardData["art"] || false; asynchronous: root.asynchronous; visible: status == Loader.Ready; sourceComponent: UbuntuShape { id: artShape; objectName: "artShape"; radius: "medium"; readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; Component.onCompleted: updateWidthHeightBindings(); onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); visible: image.status == Image.Ready; function updateWidthHeightBindings() { if (aspectSmallerThanImageAspect) { width = Qt.binding(function() { return !visible ? 0 : image.width }); height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); } else { width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); height = Qt.binding(function() { return !visible ? 0 : image.height }); } } image: Image { objectName: "artImage"; source: cardData && cardData["art"] || ""; cache: true; asynchronous: root.asynchronous; fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; readonly property real aspect: implicitWidth / implicitHeight; width: root.width; height: width / artShape.aspect; } } } }Loader { id: overlayLoader; anchors { left: artShapeHolder.left; right: artShapeHolder.right; bottom: artShapeHolder.bottom; } active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; asynchronous: root.asynchronous; visible: showHeader && status == Loader.Ready; sourceComponent: ShaderEffect { id: overlay; height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight; opacity: 0.6; property var source: ShaderEffectSource { id: shaderSource; sourceItem: artShapeLoader.item; onVisibleChanged: if (visible) scheduleUpdate(); live: false; sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); } vertexShader: " uniform highp mat4 qt_Matrix; attribute highp vec4 qt_Vertex; attribute highp vec2 qt_MultiTexCoord0; varying highp vec2 coord; void main() { coord = qt_MultiTexCoord0; gl_Position = qt_Matrix * qt_Vertex; }"; fragmentShader: " varying highp vec2 coord; uniform sampler2D source; uniform lowp float qt_Opacity; void main() { lowp vec4 tex = texture2D(source, coord); gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; }"; } }readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;Label { id: titleLabel; objectName: "titleLabel"; anchors.left: parent.left; anchors.leftMargin: units.gu(1); anchors.right: parent.right; anchors.top: overlayLoader.top; anchors.topMargin: units.gu(1); elide: Text.ElideRight; fontSize: "small"; wrapMode: Text.Wrap; maximumLineCount: 2; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "white"; visible: showHeader && overlayLoader.active; text: root.title; font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; horizontalAlignment: root.headerAlignment; }Label { id: subtitleLabel; objectName: "subtitleLabel"; anchors.left: titleLabel.left; anchors.leftMargin: titleLabel.leftMargin; anchors.right: titleLabel.right; anchors.top: titleLabel.bottom; anchors.topMargin: units.dp(2); elide: Text.ElideRight; fontSize: "small"; font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); color: "white"; visible: titleLabel.visible && titleLabel.text; text: cardData && cardData["subtitle"] || ""; font.weight: Font.Light; horizontalAlignment: root.headerAlignment; }implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);}
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property real fontScale: 1.0;
7+ property int headerAlignment: Text.AlignLeft;
8+ property int fixedHeaderHeight: -1;
9+ property size fixedArtShapeSize: Qt.size(-1, -1);
10+ readonly property string title: cardData && cardData["title"] || "";
11+ property bool asynchronous: true;
12+ property bool showHeader: true;
13+ implicitWidth: childrenRect.width;
14+readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15+Item {
16+ id: artShapeHolder;
17+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height;
18+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width;
19+ anchors.horizontalCenter: parent.horizontalCenter;
20+
21+ Loader {
22+ id: artShapeLoader;
23+ objectName: "artShapeLoader";
24+ active: cardData && cardData["art"] || false;
25+ asynchronous: root.asynchronous;
26+ visible: status == Loader.Ready;
27+ sourceComponent: UbuntuShape {
28+ id: artShape;
29+ objectName: "artShape";
30+ radius: "medium";
31+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1;
32+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
33+ Component.onCompleted: updateWidthHeightBindings();
34+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
35+ visible: image.status == Image.Ready;
36+ function updateWidthHeightBindings() {
37+ if (aspectSmallerThanImageAspect) {
38+ width = Qt.binding(function() { return !visible ? 0 : image.width });
39+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
40+ } else {
41+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
42+ height = Qt.binding(function() { return !visible ? 0 : image.height });
43+ }
44+ }
45+ image: Image {
46+ objectName: "artImage";
47+ source: cardData && cardData["art"] || "";
48+ cache: true;
49+ asynchronous: root.asynchronous;
50+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
51+ readonly property real aspect: implicitWidth / implicitHeight;
52+ width: root.width;
53+ height: width / artShape.aspect;
54+
55+ }
56+ }
57+ }
58+ }
59+Loader {
60+ id: overlayLoader;
61+ anchors {
62+ left: artShapeHolder.left;
63+ right: artShapeHolder.right;
64+ bottom: artShapeHolder.bottom;
65+ }
66+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
67+ asynchronous: root.asynchronous;
68+ visible: showHeader && status == Loader.Ready;
69+ sourceComponent: ShaderEffect {
70+ id: overlay;
71+ height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight;
72+
73+ opacity: 0.6;
74+ property var source: ShaderEffectSource {
75+ id: shaderSource;
76+ sourceItem: artShapeLoader.item;
77+ onVisibleChanged: if (visible) scheduleUpdate();
78+ live: false;
79+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
80+ }
81+ vertexShader: "
82+ uniform highp mat4 qt_Matrix;
83+ attribute highp vec4 qt_Vertex;
84+ attribute highp vec2 qt_MultiTexCoord0;
85+ varying highp vec2 coord;
86+ void main() {
87+ coord = qt_MultiTexCoord0;
88+ gl_Position = qt_Matrix * qt_Vertex;
89+ }";
90+ fragmentShader: "
91+ varying highp vec2 coord;
92+ uniform sampler2D source;
93+ uniform lowp float qt_Opacity;
94+ void main() {
95+ lowp vec4 tex = texture2D(source, coord);
96+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
97+ }";
98+ }
99+ }
100+readonly property int headerHeight: subtitleLabel.y + subtitleLabel.height - titleLabel.y + titleLabel.anchors.topMargin * 2 + subtitleLabel.anchors.topMargin;
101+Label {
102+ id: titleLabel;
103+ objectName: "titleLabel";
104+ anchors.left: parent.left;
105+ anchors.leftMargin: units.gu(1);
106+ anchors.right: parent.right;
107+ anchors.top: overlayLoader.top;
108+ anchors.topMargin: units.gu(1);
109+
110+ elide: Text.ElideRight;
111+ fontSize: "small";
112+ wrapMode: Text.Wrap;
113+ maximumLineCount: 2;
114+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
115+ color: "white";
116+ visible: showHeader && overlayLoader.active;
117+ text: root.title;
118+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
119+ horizontalAlignment: root.headerAlignment;
120+ }
121+Label {
122+ id: subtitleLabel;
123+ objectName: "subtitleLabel";
124+ anchors.left: titleLabel.left;
125+ anchors.leftMargin: titleLabel.leftMargin;
126+ anchors.right: titleLabel.right;
127+ anchors.top: titleLabel.bottom;
128+ anchors.topMargin: units.dp(2);
129+
130+ elide: Text.ElideRight;
131+ fontSize: "small";
132+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
133+ color: "white";
134+ visible: titleLabel.visible && titleLabel.text;
135+ text: cardData && cardData["subtitle"] || "";
136+ font.weight: Font.Light;
137+ horizontalAlignment: root.headerAlignment;
138+ }
139+implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);
140+}
tests/plugins/Dash/cardcreatortest.cpp+2−1
@@ -44,7 +44,7 @@ private Q_SLOTS:
4444 delete view;
4545 }
4646
47− void testKnownCases()
47+ void testKnownCases()
4848 {
4949 const QString templateString("template: ");
5050 const QString componentsString("components: ");
@@ -54,6 +54,7 @@ private Q_SLOTS:
5454 QDir d(testDirPath);
5555 const QStringList testFiles = d.entryList(QStringList() << "*.tst");
5656 foreach(const QString &testFileName, testFiles) {
57+ qDebug() << testFileName;
5758 QFile testFile(testDirPath + testFileName);
5859 QVERIFY(testFile.open(QIODevice::ReadOnly));
5960 QTextStream ts(&testFile);