@@ -16,6 +16,209 @@
17.pragma library
17.pragma library
19+var backgroundLoaderCode = 'Loader {\n\
20+ id: backgroundLoader; \n\
21+ objectName: "backgroundLoader"; \n\
22+ anchors.fill: parent; \n\
23+ asynchronous: root.asynchronous; \n\
24+ visible: status == Loader.Ready; \n\
25+ sourceComponent: UbuntuShape { \n\
26+ objectName: "background"; \n\
27+ radius: "medium"; \n\
28+ color: getColor(0) || "white"; \n\
29+ gradientColor: getColor(1) || color; \n\
30+ anchors.fill: parent; \n\
31+ image: backgroundImage.source ? backgroundImage : null; \n\
32+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; \n\
33+ property Image backgroundImage: Image { \n\
34+ objectName: "backgroundImage"; \n\
36+ if (cardData && typeof cardData["background"] === "string") return cardData["background"]; \n\
37+ else if (template && typeof template["card-background"] === "string") return template["card-background"]; \n\
41+ function getColor(index) { \n\
42+ if (cardData && typeof cardData["background"] === "object" \n\
43+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) { \n\
44+ return cardData["background"]["elements"][index]; \n\
45+ } else if (template && typeof template["card-background"] === "object" \n\
46+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) { \n\
47+ return template["card-background"]["elements"][index]; \n\
48+ } else return undefined; \n\
53+var artShapeHolderCode = 'Item { \n\
54+ id: artShapeHolder; \n\
55+ height: root.fixedArtShapeSize.height != -1 ? root.fixedArtShapeSize.height : artShapeLoader.height; \n\
56+ width: root.fixedArtShapeSize.width != -1 ? root.fixedArtShapeSize.width : artShapeLoader.width; \n\
59+ id: artShapeLoader; \n\
60+ objectName: "artShapeLoader"; \n\
61+ active: cardData && cardData["art"] || false; \n\
62+ asynchronous: root.asynchronous; \n\
63+ visible: status == Loader.Ready; \n\
64+ sourceComponent: UbuntuShape { \n\
66+ objectName: "artShape"; \n\
67+ radius: "medium"; \n\
68+ readonly property real aspect: components !== undefined ? components["art"]["aspect-ratio"] : 1; \n\
69+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect; \n\
70+ Component.onCompleted: updateWidthHeightBindings(); \n\
71+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings(); \n\
72+ visible: image.status == Image.Ready; \n\
73+ function updateWidthHeightBindings() { \n\
74+ if (aspectSmallerThanImageAspect) { \n\
75+ width = Qt.binding(function() { return !visible ? 0 : image.width }); \n\
76+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect }); \n\
78+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect }); \n\
79+ height = Qt.binding(function() { return !visible ? 0 : image.height }); \n\
83+ objectName: "artImage"; \n\
84+ source: cardData && cardData["art"] || ""; \n\
86+ asynchronous: root.asynchronous; \n\
87+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop; \n\
88+ readonly property real aspect: implicitWidth / implicitHeight; \n\
95+var overlayLoaderCode = 'Loader { \n\
96+ id: overlayLoader; \n\
98+ left: artShapeHolder.left; \n\
99+ right: artShapeHolder.right; \n\
100+ bottom: artShapeHolder.bottom; \n\
102+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
103+ asynchronous: root.asynchronous; \n\
104+ visible: showHeader && status == Loader.Ready; \n\
105+ sourceComponent: ShaderEffect { \n\
107+ height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight; \n\
109+ property var source: ShaderEffectSource { \n\
110+ id: shaderSource; \n\
111+ sourceItem: artShapeLoader.item; \n\
112+ onVisibleChanged: if (visible) scheduleUpdate(); \n\
114+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); \n\
117+ uniform highp mat4 qt_Matrix; \n\
118+ attribute highp vec4 qt_Vertex; \n\
119+ attribute highp vec2 qt_MultiTexCoord0; \n\
120+ varying highp vec2 coord; \n\
122+ coord = qt_MultiTexCoord0; \n\
123+ gl_Position = qt_Matrix * qt_Vertex; \n\
125+ fragmentShader: " \n\
126+ varying highp vec2 coord; \n\
127+ uniform sampler2D source; \n\
128+ uniform lowp float qt_Opacity; \n\
130+ lowp vec4 tex = texture2D(source, coord); \n\
131+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \n\
136+var headerRowCode = 'Row { \n\
138+ objectName: "outerRow"; \n\
139+ property real margins: units.gu(1); \n\
140+ spacing: margins; \n\
143+ anchors.right: parent.right; \n\
144+ anchors.margins: margins;\n';
146+var mascotShapeLoaderCode = 'Loader { \n\
147+ id: mascotShapeLoader; \n\
148+ objectName: "mascotShapeLoader"; \n\
149+ asynchronous: root.asynchronous; \n\
150+ active: mascotImage.status === Image.Ready; \n\
151+ visible: showHeader && active && status == Loader.Ready; \n\
152+ width: units.gu(6); \n\
153+ height: units.gu(5.625); \n\
154+ sourceComponent: UbuntuShape { image: mascotImage } \n\
158+var mascotImageCode = 'Image { \n\
159+ id: mascotImage; \n\
160+ objectName: "mascotImage"; \n\
162+ readonly property int maxSize: Math.max(width, height) * 4; \n\
163+ source: cardData && cardData["mascot"]; \n\
164+ width: units.gu(6); \n\
165+ height: units.gu(5.625); \n\
166+ sourceSize { width: maxSize; height: maxSize } \n\
167+ fillMode: Image.PreserveAspectCrop; \n\
168+ horizontalAlignment: Image.AlignHCenter; \n\
169+ verticalAlignment: Image.AlignVCenter; \n\
173+var titleLabelCode = 'Label { \n\
175+ objectName: "titleLabel"; \n\
177+ elide: Text.ElideRight; \n\
178+ fontSize: "small"; \n\
179+ wrapMode: Text.Wrap; \n\
180+ maximumLineCount: 2; \n\
181+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
183+ visible: showHeader %3; \n\
184+ text: root.title; \n\
185+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \n\
186+ horizontalAlignment: root.headerAlignment; \n\
189+var subtitleLabelCode = 'Label { \n\
190+ id: subtitleLabel; \n\
191+ objectName: "subtitleLabel"; \n\
193+ elide: Text.ElideRight; \n\
194+ fontSize: "small"; \n\
195+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
197+ visible: titleLabel.visible && titleLabel.text; \n\
198+ text: cardData && cardData["subtitle"] || ""; \n\
199+ font.weight: Font.Light; \n\
200+ horizontalAlignment: root.headerAlignment; \n\
203+var summaryLabelCode = 'Label { \n\
205+ objectName: "summaryLabel"; \n\
208+ left: parent.left; \n\
209+ right: parent.right; \n\
210+ margins: units.gu(1); \n\
213+ wrapMode: Text.Wrap; \n\
214+ maximumLineCount: 5; \n\
215+ elide: Text.ElideRight; \n\
216+ text: cardData && cardData["summary"] || ""; \n\
217+ height: text ? implicitHeight : 0; \n\
218+ fontSize: "small"; \n\
19function cardString(template, components) {
222function cardString(template, components) {
20 var code;
223 var code;
21 code = 'AbstractButton { \n\
224 code = 'AbstractButton { \n\
@@ -44,144 +247,38 @@ function cardString(template, components) {
44 var hasHeaderRow = hasMascot && hasTitle;
247 var hasHeaderRow = hasMascot && hasTitle;
46 if (hasBackground) {
249 if (hasBackground) {
47− code += 'Loader {\n\
250+ code += backgroundLoaderCode;
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\
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\
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\
82 if (hasArt) {
253 if (hasArt) {
83− code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n'
254+ code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
84− var imageWidthHeight;
255+
256+ var imageWidthHeightCode;
85 if (isHorizontal) {
258 if (isHorizontal) {
259+ anchors = 'anchors.left: parent.left;';
86 if (hasMascot || hasTitle) {
260 if (hasMascot || hasTitle) {
87− imageWidthHeight = 'width: height * artShape.aspect; \n\
261+ imageWidthHeightCode = 'width: height * artShape.aspect; \n\
88− height: headerHeight;\n'
262+ height: headerHeight;\n';
90 // This side of the else is a bit silly, who wants an horizontal layout without mascot and title?
264 // This side of the else is a bit silly, who wants an horizontal layout without mascot and title?
91 // So we define a "random" height of the image height + 2 gu for the margins
265 // So we define a "random" height of the image height + 2 gu for the margins
92− imageWidthHeight = 'width: height * artShape.aspect; \n\
266+ imageWidthHeightCode = 'width: height * artShape.aspect; \n\
93− height: units.gu(7.625)';
267+ height: units.gu(7.625)';
270+ anchors = 'anchors.horizontalCenter: parent.horizontalCenter;';
96− imageWidthHeight = 'width: root.width; \n\
271+ imageWidthHeightCode = 'width: root.width; \n\
97− height: width / artShape.aspect;\n'
272+ height: width / artShape.aspect;\n';
99− code += 'Item { \n\
274+
100− id: artShapeHolder; \n\
275+ code += artShapeHolderCode.arg(anchors).arg(imageWidthHeightCode);
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\
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\
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\
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\
129− objectName: "artImage"; \n\
130− source: cardData && cardData["art"] || ""; \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\
141 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
277 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
144 if (headerAsOverlay) {
280 if (headerAsOverlay) {
145− code += 'Loader { \n\
281+ code += overlayLoaderCode;
146− id: overlayLoader; \n\
148− left: artShapeHolder.left; \n\
149− right: artShapeHolder.right; \n\
150− bottom: artShapeHolder.bottom; \n\
152− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false; \n\
153− asynchronous: root.asynchronous; \n\
154− visible: showHeader && status == Loader.Ready; \n\
155− sourceComponent: ShaderEffect { \n\
157− height: fixedHeaderHeight != -1 ? fixedHeaderHeight : headerHeight; \n\
159− property var source: ShaderEffectSource { \n\
160− id: shaderSource; \n\
161− sourceItem: artShapeLoader.item; \n\
162− onVisibleChanged: if (visible) scheduleUpdate(); \n\
164− sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height); \n\
167− uniform highp mat4 qt_Matrix; \n\
168− attribute highp vec4 qt_Vertex; \n\
169− attribute highp vec2 qt_MultiTexCoord0; \n\
170− varying highp vec2 coord; \n\
172− coord = qt_MultiTexCoord0; \n\
173− gl_Position = qt_Matrix * qt_Vertex; \n\
175− fragmentShader: " \n\
176− varying highp vec2 coord; \n\
177− uniform sampler2D source; \n\
178− uniform lowp float qt_Opacity; \n\
180− lowp vec4 tex = texture2D(source, coord); \n\
181− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity; \n\
187 var headerVerticalAnchors;
284 var headerVerticalAnchors;
@@ -214,15 +311,7 @@ function cardString(template, components) {
215 if (hasHeaderRow) {
312 if (hasHeaderRow) {
216 code += 'readonly property int headerHeight: row.height + row.margins * 2;\n'
313 code += 'readonly property int headerHeight: row.height + row.margins * 2;\n'
219− objectName: "outerRow"; \n\
220− property real margins: units.gu(1); \n\
221− spacing: margins; \n\
222− ' + headerVerticalAnchors + '\n\
314+ code += headerRowCode.arg(headerVerticalAnchors).arg(headerLeftAnchor);
223− ' + headerLeftAnchor + '\n\
224− anchors.right: parent.right; \n\
225− anchors.margins: margins;\n';
226 } else if (hasMascot) {
315 } else if (hasMascot) {
227 code += 'readonly property int headerHeight: mascotImage.height + units.gu(1) * 2;\n'
316 code += 'readonly property int headerHeight: mascotImage.height + units.gu(1) * 2;\n'
228 } else if (hasSubtitle) {
317 } else if (hasSubtitle) {
@@ -247,33 +336,11 @@ function cardString(template, components) {
249 if (useMascotShape) {
338 if (useMascotShape) {
250− code += 'Loader { \n\
339+ code += mascotShapeLoaderCode.arg(anchors);
251− id: mascotShapeLoader; \n\
252− objectName: "mascotShapeLoader"; \n\
253− asynchronous: root.asynchronous; \n\
254− active: mascotImage.status === Image.Ready; \n\
255− visible: showHeader && active && status == Loader.Ready; \n\
256− width: units.gu(6); \n\
257− height: units.gu(5.625); \n\
258− sourceComponent: UbuntuShape { image: mascotImage } \n\
263− code += 'Image { \n\
264− id: mascotImage; \n\
265− objectName: "mascotImage"; \n\
267− readonly property int maxSize: Math.max(width, height) * 4; \n\
268− source: cardData && cardData["mascot"]; \n\
269− width: units.gu(6); \n\
270− height: units.gu(5.625); \n\
271− sourceSize { width: maxSize; height: maxSize } \n\
272− fillMode: Image.PreserveAspectCrop; \n\
273− horizontalAlignment: Image.AlignHCenter; \n\
274− verticalAlignment: Image.AlignVCenter; \n\
275− visible: showHeader && ' + (useMascotShape ? 'false' : 'true') + '; \n\
342+ var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
276− }\n';
343+ code += mascotImageCode.arg(anchors).arg(mascotImageVisible);
279 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"';
346 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : "grey"';
@@ -328,36 +395,11 @@ function cardString(template, components) {
328 anchors.topMargin: units.dp(2);\n';
395 anchors.topMargin: units.dp(2);\n';
331− code += 'Label { \n\
333− objectName: "titleLabel"; \n\
334− ' + titleAnchors + '\n\
335− elide: Text.ElideRight; \n\
336− fontSize: "small"; \n\
337− wrapMode: Text.Wrap; \n\
338− maximumLineCount: 2; \n\
339− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
340− color: ' + color + '; \n\
341− visible: showHeader ' + (headerAsOverlay ? '&& overlayLoader.active': '') + '; \n\
398+ var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
342− text: root.title; \n\
399+ code += titleLabelCode.arg(titleAnchors).arg(color).arg(titleLabelVisibleExtra);
343− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal; \n\
344− horizontalAlignment: root.headerAlignment; \n\
347 if (hasSubtitle) {
401 if (hasSubtitle) {
348− code += 'Label { \n\
349− id: subtitleLabel; \n\
350− objectName: "subtitleLabel"; \n\
351− ' + subtitleAnchors + '\n\
402+ code += subtitleLabelCode.arg(subtitleAnchors).arg(color);
352− elide: Text.ElideRight; \n\
353− fontSize: "small"; \n\
354− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
355− color: ' + color + '; \n\
356− visible: titleLabel.visible && titleLabel.text; \n\
357− text: cardData && cardData["subtitle"] || ""; \n\
358− font.weight: Font.Light; \n\
359− horizontalAlignment: root.headerAlignment; \n\
362 // Close Column
404 // Close Column
363 if (hasMascot)
405 if (hasMascot)
@@ -380,33 +422,19 @@ function cardString(template, components) {
380 else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
422 else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
381 else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
423 else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
382 else summaryTopAnchor = "parent.top";
424 else summaryTopAnchor = "parent.top";
383 var color;
426 var color;
384 if (hasBackground) {
427 if (hasBackground) {
385 color = summaryColorWithBackground;
428 color = summaryColorWithBackground;
387 color = '"grey"';
430 color = '"grey"';
389− code += 'Label { \n\
432+
390− id: summary; \n\
433+ var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
391− objectName: "summaryLabel"; \n\
434+
392− anchors { \n\
435+ code += summaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
393− top: ' + summaryTopAnchor + '; \n\
394− left: parent.left; \n\
395− right: parent.right; \n\
396− margins: units.gu(1); \n\
397− topMargin: ' + (hasMascot || hasSubtitle ? 'anchors.margins' : 0) + '; \n\
399− wrapMode: Text.Wrap; \n\
400− maximumLineCount: 5; \n\
401− elide: Text.ElideRight; \n\
402− text: cardData && cardData["summary"] || ""; \n\
403− height: text ? implicitHeight : 0; \n\
404− fontSize: "small"; \n\
405− color: ' + color + '; \n\
409− // Close the AbstractButton
410 if (hasSummary) {
438 if (hasSummary) {
411 code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
439 code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
412 } else if (hasHeaderRow) {
440 } else if (hasHeaderRow) {
@@ -418,6 +446,7 @@ function cardString(template, components) {
418 } else if (hasTitle) {
446 } else if (hasTitle) {
419 code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);\n';
447 code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);\n';
449+ // Close the AbstractButton
421 code += '}\n';
450 code += '}\n';
423 return code;
452 return code;