on xenial help sign in
Add emblem support in dash cards.

Approved by: Michał Sawicz

commit6ae85356c0616debc745345ee1d03e5d97275de6 authorAndrea Cimitan <andrea.cimitan@gmail.com> dateMon 11 Aug 2014, 19:02 (12 years ago) parent39614c9ca354db
18 files changed+420−283
plugins/Dash/CardCreator.js+186−146 tests/mocks/Unity/fake_categories.cpp+6−2 tests/plugins/Dash/cardcreator/1.res+4−6 tests/plugins/Dash/cardcreator/1.tst+1−1 tests/plugins/Dash/cardcreator/2.res+25−17 tests/plugins/Dash/cardcreator/2.tst+1−1 tests/plugins/Dash/cardcreator/3.res+9−12 tests/plugins/Dash/cardcreator/3.tst+1−1 tests/plugins/Dash/cardcreator/4.res+23−15 tests/plugins/Dash/cardcreator/4.tst+1−1 tests/plugins/Dash/cardcreator/5.res+11−13 tests/plugins/Dash/cardcreator/5.tst+1−1 tests/plugins/Dash/cardcreator/6.res+97−44 tests/plugins/Dash/cardcreator/6.tst+2−2 tests/plugins/Dash/cardcreator/7.res+32−20 tests/qmltests/Dash/CardHelpers.js+1 tests/qmltests/Dash/artwork/emblem.png tests/qmltests/Dash/tst_Card.qml+19−1
plugins/Dash/CardCreator.js+186−146
@@ -138,10 +138,9 @@ var kOverlayLoaderCode = 'Loader { \n\
138138 } \n\
139139 }\n';
140140
141−// %1 is used as anchors of row
142−// %2 is used as first child of the row
143−// %3 is used as second child of the row
144−var kHeaderRow2Code = 'Row { \n\
141+// multiple row version of HeaderRowCode
142+function kHeaderRowCodeGenerator() {
143+var kHeaderRowCodeTemplate = 'Row { \n\
145144 id: row; \n\
146145 objectName: "outerRow"; \n\
147146 property real margins: units.gu(1); \n\
@@ -149,58 +148,30 @@ var kHeaderRow2Code = 'Row { \n\
149148 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; \n\
150149 anchors { %1 } \n\
151150 anchors.right: parent.right; \n\
152− anchors.margins: margins;\n\
153− data: [ %2\n\
154− ,\n\
155− %3 \n\
151+ anchors.margins: margins; \n\
152+ anchors.rightMargin: 0; \n\
153+ data: [ \n\
154+ %2 \n\
156155 ] \n\
157156 }\n';
157+ var args = Array.prototype.slice.call(arguments);
158+ var code = kHeaderRowCodeTemplate.arg(args.shift()).arg(args.join(',\n'));
159+ return code;
160+}
158161
159−// %1 is used as anchors of row
160−// %2 is used as first child of the row
161−// %3 is used as second child of the row
162−// %4 is used as third child of the row
163−var kHeaderRow3Code = 'Row { \n\
164− id: row; \n\
165− objectName: "outerRow"; \n\
166− property real margins: units.gu(1); \n\
167− spacing: margins; \n\
168− height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; \n\
169− anchors { %1 } \n\
170− anchors.right: parent.right; \n\
171− anchors.margins: margins;\n\
172− data: [ %2\n\
173− ,\n\
162+// multiple item version of kHeaderContainerCode
163+function kHeaderContainerCodeGenerator() {
164+ var headerContainerCodeTemplate = 'Item { \n\
165+ id: headerTitleContainer; \n\
166+ anchors { %1 } \n\
167+ width: parent.width - x; \n\
168+ implicitHeight: %2; \n\
169+ data: [ \n\
174170 %3 \n\
175− ,\n\
176− %4 \n\
177− ] \n\
178− }\n';
179−
180−// %1 is used as first child of the column
181−// %2 is used as second child of the column
182−var kHeaderColumnCode = 'Column { \n\
183− anchors.verticalCenter: parent.verticalCenter; \n\
184− spacing: units.dp(2); \n\
185− width: parent.width - x;\n\
186− data: [ %1\n\
187− ,\n\
188− %2 \n\
189− ] \n\
171+ ]\n\
190172 }\n';
191−
192−// multiple column version of kHeaderColumnCode.
193−function kHeaderColumnCodeGenerator() {
194− var headerColumnCodeTemplate = 'Column { \n\
195− anchors.verticalCenter: parent.verticalCenter; \n\
196− spacing: units.dp(2); \n\
197− width: parent.width - x;\n\
198− data: [ \n\
199− %1 \n\
200− ]\n\
201− }\n';
202173 var args = Array.prototype.slice.call(arguments);
203− var code = headerColumnCodeTemplate.arg(args.join(',\n'));
174+ var code = headerContainerCodeTemplate.arg(args.shift()).arg(args.shift()).arg(args.join(',\n'));
204175 return code;
205176}
206177
@@ -224,7 +195,7 @@ var kMascotImageCode = 'Image { \n\
224195 objectName: "mascotImage"; \n\
225196 anchors { %1 } \n\
226197 readonly property int maxSize: Math.max(width, height) * 4; \n\
227− source: cardData && cardData["mascot"]; \n\
198+ source: cardData && cardData["mascot"] || ""; \n\
228199 width: units.gu(6); \n\
229200 height: units.gu(5.625); \n\
230201 sourceSize { width: maxSize; height: maxSize } \n\
@@ -253,6 +224,22 @@ var kTitleLabelCode = 'Label { \n\
253224 horizontalAlignment: root.headerAlignment; \n\
254225 }\n';
255226
227+// %1 is used as extra anchors of emblemIcon
228+// %2 is used as color of emblemIcon
229+var kEmblemIconCode = 'Icon { \n\
230+ id: emblemIcon; \n\
231+ objectName: "emblemIcon"; \n\
232+ anchors { \n\
233+ bottom: titleLabel.baseline; \n\
234+ right: parent.right; \n\
235+ %1
236+ } \n\
237+ source: cardData && cardData["emblem"] || ""; \n\
238+ color: %2; \n\
239+ width: height; \n\
240+ height: source != "" ? titleLabel.font.pixelSize : 0; \n\
241+ }\n';
242+
256243// %1 is used as anchors of touchdown effect
257244var kTouchdownCode = 'UbuntuShape { \n\
258245 id: touchdown; \n\
@@ -269,6 +256,7 @@ var kSubtitleLabelCode = 'Label { \n\
269256 id: subtitleLabel; \n\
270257 objectName: "subtitleLabel"; \n\
271258 anchors { %1 } \n\
259+ anchors.topMargin: units.dp(2); \n\
272260 elide: Text.ElideRight; \n\
273261 fontSize: "small"; \n\
274262 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale); \n\
@@ -280,12 +268,13 @@ var kSubtitleLabelCode = 'Label { \n\
280268 }\n';
281269
282270// %1 is used as anchors of attributesRow
271+// %2 is used as color of attributesRow
283272var kAttributesRowCode = 'CardAttributes { \n\
284273 id: attributesRow; \n\
285274 objectName: "attributesRow"; \n\
286275 anchors { %1 } \n\
287276 color: %2; \n\
288− model: cardData["attributes"]; \n\
277+ model: cardData && cardData["attributes"]; \n\
289278 }\n';
290279
291280// %1 is used as top anchor of summary
@@ -335,10 +324,11 @@ function cardString(template, components) {
335324 var hasBackground = !isHorizontal && (template["card-background"] || components["background"] || artAndSummary);
336325 var hasTitle = components["title"] || false;
337326 var hasMascot = components["mascot"] || false;
327+ var hasEmblem = components["emblem"] && !(hasMascot && template["card-size"] === "small") || false;
338328 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
339329 var hasSubtitle = hasTitle && components["subtitle"] || false;
340330 var hasHeaderRow = hasMascot && hasTitle;
341− var hasAttributes = hasTitle && components["attributes"] || false;
331+ var hasAttributes = hasTitle && components["attributes"]["field"] || false;
342332
343333 if (hasBackground) {
344334 code += kBackgroundLoaderCode;
@@ -349,9 +339,9 @@ function cardString(template, components) {
349339 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
350340
351341 var widthCode, heightCode;
352− var anchors;
342+ var artAnchors;
353343 if (isHorizontal) {
354− anchors = 'left: parent.left';
344+ artAnchors = 'left: parent.left';
355345 if (hasMascot || hasTitle) {
356346 widthCode = 'height * artShape.aspect'
357347 heightCode = 'headerHeight + 2 * units.gu(1)';
@@ -362,12 +352,12 @@ function cardString(template, components) {
362352 heightCode = 'units.gu(7.625)';
363353 }
364354 } else {
365− anchors = 'horizontalCenter: parent.horizontalCenter;';
355+ artAnchors = 'horizontalCenter: parent.horizontalCenter;';
366356 widthCode = 'root.width'
367357 heightCode = 'width / artShape.aspect';
368358 }
369359
370− code += kArtShapeHolderCode.arg(anchors).arg(widthCode).arg(heightCode);
360+ code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode);
371361 } else {
372362 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
373363 }
@@ -404,6 +394,8 @@ function cardString(template, components) {
404394 headerLeftAnchor = 'left: parent.left;\n';
405395 }
406396
397+ var touchdownOnArtShape = !hasBackground && hasArt && !hasMascot && !hasSummary;
398+
407399 if (hasHeaderRow) {
408400 code += 'readonly property int headerHeight: row.height;\n'
409401 } else if (hasMascot) {
@@ -424,191 +416,239 @@ function cardString(template, components) {
424416 code += 'readonly property int headerHeight: 0;\n'
425417 }
426418
427− var mascotShapeCode = "";
428− var mascotCode = "";
419+ var mascotShapeCode = '';
420+ var mascotCode = '';
429421 if (hasMascot) {
430422 var useMascotShape = !hasBackground && !headerAsOverlay;
431− var anchors = "";
423+ var mascotAnchors = '';
432424 if (!hasHeaderRow) {
433− anchors += headerLeftAnchor;
434− anchors += headerVerticalAnchors;
425+ mascotAnchors += headerLeftAnchor;
426+ mascotAnchors += headerVerticalAnchors;
435427 if (!headerLeftAnchorHasMargin) {
436− anchors += 'leftMargin: units.gu(1);\n'
428+ mascotAnchors += 'leftMargin: units.gu(1);\n'
437429 }
438430 } else {
439− anchors = "verticalCenter: parent.verticalCenter;"
431+ mascotAnchors = 'verticalCenter: parent.verticalCenter;'
440432 }
441433
442434 if (useMascotShape) {
443− mascotShapeCode = kMascotShapeLoaderCode.arg(anchors);
435+ mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
444436 }
445437
446438 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
447− mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
439+ mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
448440 }
449441
450442 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
451443
452− var titleSubtitleCode = "";
444+ var hasTitleContainer = hasTitle && (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes)));
445+ var titleSubtitleCode = '';
453446 if (hasTitle) {
454− var color;
447+ var titleColor;
455448 if (headerAsOverlay) {
456− color = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
449+ titleColor = 'overlayLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey")';
457450 } else if (hasSummary) {
458− color = 'summary.color';
451+ titleColor = 'summary.color';
459452 } else if (hasBackground) {
460− color = summaryColorWithBackground;
453+ titleColor = summaryColorWithBackground;
461454 } else {
462− color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
455+ titleColor = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
463456 }
464457
465458 var titleAnchors;
466459 var subtitleAnchors;
467460 var attributesAnchors;
468− if (hasMascot && (hasSubtitle || hasAttributes)) {
469− // Using row + column
470− titleAnchors = 'left: parent.left; right: parent.right';
471− subtitleAnchors = titleAnchors;
472− attributesAnchors = subtitleAnchors;
461+ var titleContainerAnchors;
462+ var titleRightAnchor;
463+
464+ var extraRightAnchor = '';
465+ var extraLeftAnchor = '';
466+ if (!touchdownOnArtShape) {
467+ extraRightAnchor = 'rightMargin: units.gu(1); \n';
468+ extraLeftAnchor = 'leftMargin: units.gu(1); \n';
469+ }
470+
471+ if (hasMascot) {
472+ titleContainerAnchors = 'verticalCenter: parent.verticalCenter; ';
473+ } else {
474+ titleContainerAnchors = 'right: parent.right; ';
475+ titleContainerAnchors += headerLeftAnchor;
476+ titleContainerAnchors += headerVerticalAnchors;
477+ if (!headerLeftAnchorHasMargin) {
478+ titleContainerAnchors += extraLeftAnchor;
479+ }
480+ }
481+ if (hasEmblem) {
482+ titleRightAnchor = 'right: emblemIcon.left; \n\
483+ rightMargin: emblemIcon.width > 0 ? units.gu(0.5) : 0; \n';
484+ } else {
485+ titleRightAnchor = 'right: parent.right; \n'
486+ titleRightAnchor += extraRightAnchor;
487+ }
488+
489+ if (hasTitleContainer) {
490+ // Using headerTitleContainer
491+ titleAnchors = titleRightAnchor;
492+ titleAnchors += 'left: parent.left; \n\
493+ top: parent.top;';
494+ subtitleAnchors = 'right: parent.right; \n\
495+ left: parent.left; \n';
496+ subtitleAnchors += extraRightAnchor;
497+ if (hasSubtitle) {
498+ attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
499+ subtitleAnchors += 'top: titleLabel.bottom;\n';
500+ } else {
501+ attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
502+ }
473503 } else if (hasMascot) {
474− // Using row + label
475− titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
504+ // Using row + titleContainer
505+ titleAnchors = 'verticalCenter: parent.verticalCenter;\n';
476506 } else {
477507 if (headerAsOverlay) {
478508 // Using anchors to the overlay
479− titleAnchors = 'left: parent.left; \n\
480− leftMargin: units.gu(1); \n\
481− right: parent.right; \n\
482− rightMargin: units.gu(1); \n\
483− top: overlayLoader.top; \n\
484− topMargin: units.gu(1);\n';
509+ titleAnchors = titleRightAnchor;
510+ titleAnchors += 'left: parent.left; \n\
511+ leftMargin: units.gu(1); \n\
512+ top: overlayLoader.top; \n\
513+ topMargin: units.gu(1);\n';
485514 } else {
486515 // Using anchors to the mascot/parent
487− titleAnchors = "right: parent.right;\n";
488− titleAnchors += "rightMargin: units.gu(1);\n";
516+ titleAnchors = titleRightAnchor;
489517 titleAnchors += headerLeftAnchor;
490518 titleAnchors += headerVerticalAnchors;
491519 if (!headerLeftAnchorHasMargin) {
492− titleAnchors += 'leftMargin: units.gu(1);\n'
520+ titleAnchors += extraLeftAnchor;
493521 }
494522 }
495523 subtitleAnchors = 'left: titleLabel.left; \n\
496− leftMargin: titleLabel.leftMargin; \n\
497− right: titleLabel.right; \n\
498− rightMargin: titleLabel.rightMargin; \n\
499− top: titleLabel.bottom; \n\
500− topMargin: units.dp(2);\n';
524+ leftMargin: titleLabel.leftMargin; \n';
525+ subtitleAnchors += extraRightAnchor;
526+ if (hasEmblem) {
527+ // using container
528+ subtitleAnchors += 'right: parent.right; \n';
529+ } else {
530+ subtitleAnchors += 'right: titleLabel.right; \n';
531+ }
532+
501533 if (hasSubtitle) {
502− attributesAnchors = 'left: subtitleLabel.left; \n\
503− leftMargin: subtitleLabel.leftMargin; \n\
504− right: subtitleLabel.right; \n\
505− rightMargin: subtitleLabel.rightMargin; \n\
506− top: subtitleLabel.bottom; \n\
507− topMargin: units.dp(2);\n';
534+ attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
535+ subtitleAnchors += 'top: titleLabel.bottom;\n';
508536 } else {
509− attributesAnchors = subtitleAnchors;
537+ attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
510538 }
511539 }
512540
541+ // code for different elements
513542 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
514− var titleCode = kTitleLabelCode.arg(titleAnchors).arg(color).arg(titleLabelVisibleExtra);
515− var subtitleCode = "";
543+ var titleCode = kTitleLabelCode.arg(titleAnchors).arg(titleColor).arg(titleLabelVisibleExtra);
544+ var subtitleCode;
545+ var attributesCode;
546+
547+ // code for the title container
548+ var containerCode = [];
549+ var containerHeight = 'titleLabel.height';
550+ containerCode.push(titleCode);
516551 if (hasSubtitle) {
517− subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
552+ subtitleCode = kSubtitleLabelCode.arg(subtitleAnchors).arg(titleColor);
553+ containerCode.push(subtitleCode);
554+ containerHeight += ' + subtitleLabel.height';
555+ }
556+ if (hasEmblem) {
557+ containerCode.push(kEmblemIconCode.arg(extraRightAnchor).arg(titleColor));
558+ }
559+ if (hasAttributes) {
560+ attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(titleColor);
561+ containerCode.push(attributesCode);
562+ containerHeight += ' + attributesRow.height';
518563 }
519564
520− if (hasMascot && (hasSubtitle || hasAttributes)) {
521− // If using row + column wrap the code in the column
522− titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
523− if (hasSubtitle && hasAttributes) {
524− var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
525− titleSubtitleCode = kHeaderColumnCodeGenerator(titleCode, subtitleCode, attributesCode);
526− } else if (hasSubtitle) {
527− titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
528− } else if (hasAttributes) {
529− var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
530− titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(attributesCode);
531− }
565+ if (hasTitleContainer) {
566+ // use container
567+ titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, containerHeight, containerCode);
532568 } else {
569+ // no container
533570 titleSubtitleCode = titleCode;
534571 if (hasSubtitle) {
535− titleSubtitleCode = titleSubtitleCode + subtitleCode;
572+ titleSubtitleCode += subtitleCode;
536573 }
537574 if (hasAttributes) {
538− var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
539− titleSubtitleCode = titleSubtitleCode + attributesCode;
575+ titleSubtitleCode += attributesCode;
540576 }
541577 }
542578 }
543579
544580 if (hasHeaderRow) {
545− if (mascotShapeCode != "") {
546− code += kHeaderRow3Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotShapeCode).arg(mascotCode).arg(titleSubtitleCode);
547− } else {
548− code += kHeaderRow2Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotCode).arg(titleSubtitleCode);
581+ var rowCode = [mascotCode, titleSubtitleCode];
582+ if (mascotShapeCode != '') {
583+ rowCode.unshift(mascotShapeCode);
549584 }
585+ code += kHeaderRowCodeGenerator(headerVerticalAnchors + headerLeftAnchor, rowCode)
550586 } else {
551587 code += mascotShapeCode + mascotCode + titleSubtitleCode;
552588 }
553589
554590 if (hasSummary) {
555591 var summaryTopAnchor;
556− if (isHorizontal && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
557− else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
558− else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
559− else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
560− else if (hasAttributes) summaryTopAnchor = "attributesRow.bottom";
561− else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
562− else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
563− else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
564− else summaryTopAnchor = "parent.top";
565−
566− var color;
592+ if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
593+ else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
594+ else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
595+ else if (hasTitleContainer) summaryTopAnchor = 'headerTitleContainer.bottom';
596+ else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
597+ else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
598+ else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
599+ else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
600+ else if (hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
601+ else summaryTopAnchor = 'parent.top';
602+
603+ var summaryColor;
567604 if (hasBackground) {
568− color = summaryColorWithBackground;
605+ summaryColor = summaryColorWithBackground;
569606 } else {
570− color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
607+ summaryColor = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
571608 }
572609
573610 var summaryTopMargin = (hasMascot || hasSubtitle || hasAttributes ? 'anchors.margins' : '0');
574611
575− code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
612+ code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(summaryColor);
576613 }
577614
578615 var touchdownAnchors;
579616 if (hasBackground) {
580617 touchdownAnchors = 'fill: backgroundLoader';
581− } else if (hasArt && !hasMascot && !hasSummary) {
618+ } else if (touchdownOnArtShape) {
582619 touchdownAnchors = 'fill: artShapeHolder';
583620 } else {
584621 touchdownAnchors = 'fill: root'
585622 }
586623 code += kTouchdownCode.arg(touchdownAnchors);
587624
625+ var implicitHeight = 'implicitHeight: ';
588626 if (hasSummary) {
589− code += 'implicitHeight: summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
627+ implicitHeight += 'summary.y + summary.height + (summary.text ? units.gu(1) : 0);\n';
590628 } else if (hasHeaderRow) {
591− code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
629+ implicitHeight += 'row.y + row.height + units.gu(1);\n';
592630 } else if (hasMascot) {
593− code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
631+ implicitHeight += 'mascotImage.y + mascotImage.height;\n';
632+ } else if (hasTitleContainer) {
633+ implicitHeight += 'headerTitleContainer.y + headerTitleContainer.height + units.gu(1);\n';
594634 } else if (hasAttributes) {
595− code += 'implicitHeight: attributesRow.y + attributesRow.height + units.gu(1);\n';
635+ implicitHeight += 'attributesRow.y + attributesRow.height + units.gu(1);\n';
596636 } else if (hasSubtitle) {
597− code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
637+ implicitHeight += 'subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
598638 } else if (hasTitle) {
599− code += 'implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);\n';
639+ implicitHeight += 'titleLabel.y + titleLabel.height + units.gu(1);\n';
600640 } else if (hasArt) {
601− code += 'implicitHeight: artShapeHolder.height;\n';
641+ implicitHeight += 'artShapeHolder.height;\n';
602642 }
603643 // Close the AbstractButton
604− code += '}\n';
644+ code += implicitHeight + '}\n';
605645
606646 return code;
607647}
608648
609649function createCardComponent(parent, template, components) {
610650 var imports = 'import QtQuick 2.2; \n\
611− import Ubuntu.Components 0.1; \n\
651+ import Ubuntu.Components 1.1; \n\
612652 import Dash 0.1;\n';
613653 var card = cardString(template, components);
614654 var code = imports + 'Component {\n' + card + '}\n';
tests/mocks/Unity/fake_categories.cpp+6−2
@@ -109,11 +109,13 @@ Categories::data(const QModelIndex& index, int role) const
109109 }
110110 case RoleComponents:
111111 {
112− QVariantMap map, artMap;
112+ QVariantMap map, artMap, attributeMap;
113113 artMap["aspect-ratio"] = "1.0";
114114 artMap["field"] = "art";
115115 map["art"] = artMap;
116116 map["title"] = "HOLA";
117+ attributeMap["field"] = "attribute";
118+ map["attributes"] = attributeMap;
117119 return map;
118120 }
119121 case RoleHeaderLink:
@@ -156,7 +158,7 @@ Categories::data(const QModelIndex& index, int role) const
156158 }
157159 case RoleComponents:
158160 {
159− QVariantMap map, artMap;
161+ QVariantMap map, artMap, attributeMap;
160162 if (index.row() % 2 != 0) {
161163 artMap["aspect-ratio"] = QString("1.%1").arg(index.row());
162164 } else {
@@ -166,6 +168,8 @@ Categories::data(const QModelIndex& index, int role) const
166168 map["art"] = artMap;
167169 map["title"] = "HOLA";
168170 map["subtitle"] = "HOLA";
171+ attributeMap["field"] = "attribute";
172+ map["attributes"] = attributeMap;
169173 return map;
170174 }
171175 case RoleHeaderLink:
tests/plugins/Dash/cardcreator/1.res+4−6
@@ -61,12 +61,10 @@ Label {
6161 id: titleLabel;
6262 objectName: "titleLabel";
6363 anchors { right: parent.right;
64−rightMargin: units.gu(1);
65−left: parent.left;
66−top: artShapeHolder.bottom;
67− topMargin: units.gu(1);
68−leftMargin: units.gu(1);
69− }
64+ left: parent.left;
65+ top: artShapeHolder.bottom;
66+ topMargin: units.gu(1);
67+ }
7068 elide: Text.ElideRight;
7169 fontSize: "small";
7270 wrapMode: Text.Wrap;
tests/plugins/Dash/cardcreator/1.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1.6,"field":"art"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1.6,"field":"art"},"title":{"field":"title"},"attributes":{}}
33result: 1.res
tests/plugins/Dash/cardcreator/2.res+25−17
@@ -55,17 +55,19 @@ Row {
5555 spacing: margins;
5656 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
5757 anchors { top: parent.top;
58− topMargin: units.gu(1);
59−left: parent.left;
60− }
58+ topMargin: units.gu(1);
59+ left: parent.left;
60+ }
6161 anchors.right: parent.right;
6262 anchors.margins: margins;
63− data: [ Image {
63+ anchors.rightMargin: 0;
64+ data: [
65+Image {
6466 id: mascotImage;
6567 objectName: "mascotImage";
6668 anchors { verticalCenter: parent.verticalCenter; }
6769 readonly property int maxSize: Math.max(width, height) * 4;
68− source: cardData && cardData["mascot"];
70+ source: cardData && cardData["mascot"] || "";
6971 width: units.gu(6);
7072 height: units.gu(5.625);
7173 sourceSize { width: maxSize; height: maxSize }
@@ -74,16 +76,19 @@ left: parent.left;
7476 verticalAlignment: Image.AlignVCenter;
7577 visible: showHeader;
7678 }
77−
78− ,
79− Column {
80− anchors.verticalCenter: parent.verticalCenter;
81− spacing: units.dp(2);
82− width: parent.width - x;
83− data: [ Label {
79+,Item {
80+ id: headerTitleContainer;
81+ anchors { verticalCenter: parent.verticalCenter; }
82+ width: parent.width - x;
83+ implicitHeight: titleLabel.height + subtitleLabel.height;
84+ data: [
85+ Label {
8486 id: titleLabel;
8587 objectName: "titleLabel";
86− anchors { left: parent.left; right: parent.right }
88+ anchors { right: parent.right;
89+ rightMargin: units.gu(1);
90+ left: parent.left;
91+ top: parent.top; }
8792 elide: Text.ElideRight;
8893 fontSize: "small";
8994 wrapMode: Text.Wrap;
@@ -95,12 +100,15 @@ left: parent.left;
95100 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
96101 horizontalAlignment: root.headerAlignment;
97102 }
98−
99− ,
100− Label {
103+ ,Label {
101104 id: subtitleLabel;
102105 objectName: "subtitleLabel";
103− anchors { left: parent.left; right: parent.right }
106+ anchors { right: parent.right;
107+ left: parent.left;
108+ rightMargin: units.gu(1);
109+ top: titleLabel.bottom;
110+ }
111+ anchors.topMargin: units.dp(2);
104112 elide: Text.ElideRight;
105113 fontSize: "small";
106114 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
tests/plugins/Dash/cardcreator/2.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{}}
33result: 2.res
tests/plugins/Dash/cardcreator/3.res+9−12
@@ -61,12 +61,10 @@ Label {
6161 id: titleLabel;
6262 objectName: "titleLabel";
6363 anchors { right: parent.right;
64−rightMargin: units.gu(1);
65−left: parent.left;
66−top: artShapeHolder.bottom;
67− topMargin: units.gu(1);
68−leftMargin: units.gu(1);
69− }
64+ left: parent.left;
65+ top: artShapeHolder.bottom;
66+ topMargin: units.gu(1);
67+ }
7068 elide: Text.ElideRight;
7169 fontSize: "small";
7270 wrapMode: Text.Wrap;
@@ -82,12 +80,11 @@ Label {
8280 id: subtitleLabel;
8381 objectName: "subtitleLabel";
8482 anchors { left: titleLabel.left;
85− leftMargin: titleLabel.leftMargin;
86− right: titleLabel.right;
87− rightMargin: titleLabel.rightMargin;
88− top: titleLabel.bottom;
89− topMargin: units.dp(2);
90− }
83+ leftMargin: titleLabel.leftMargin;
84+ right: titleLabel.right;
85+ top: titleLabel.bottom;
86+ }
87+ anchors.topMargin: units.dp(2);
9188 elide: Text.ElideRight;
9289 fontSize: "small";
9390 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
tests/plugins/Dash/cardcreator/3.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":0.75,"field":"art"},"subtitle":{"field":"price"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":0.75,"field":"art"},"subtitle":{"field":"price"},"title":{"field":"title"},"attributes":{}}
33result: 3.res
tests/plugins/Dash/cardcreator/4.res+23−15
@@ -27,7 +27,9 @@ Row {
2727}
2828 anchors.right: parent.right;
2929 anchors.margins: margins;
30−data: [ Loader {
30+ anchors.rightMargin: 0;
31+data: [
32+Loader {
3133 id: mascotShapeLoader;
3234 objectName: "mascotShapeLoader";
3335 asynchronous: root.asynchronous;
@@ -39,13 +41,12 @@ data: [ Loader {
3941 anchors { verticalCenter: parent.verticalCenter; }
4042 }
4143
42−,
43−Image {
44+,Image {
4445 id: mascotImage;
4546 objectName: "mascotImage";
4647 anchors { verticalCenter: parent.verticalCenter; }
4748 readonly property int maxSize: Math.max(width, height) * 4;
48− source: cardData && cardData["mascot"];
49+ source: cardData && cardData["mascot"] || "";
4950 width: units.gu(6);
5051 height: units.gu(5.625);
5152 sourceSize { width: maxSize; height: maxSize }
@@ -55,15 +56,19 @@ Image {
5556 visible: false;
5657 }
5758
58−,
59−Column {
60− anchors.verticalCenter: parent.verticalCenter;
61− spacing: units.dp(2);
62− width: parent.width - x;
63−data: [ Label {
59+,Item {
60+ id: headerTitleContainer;
61+ anchors { verticalCenter: parent.verticalCenter; }
62+ width: parent.width - x;
63+ implicitHeight: titleLabel.height + subtitleLabel.height;
64+ data: [
65+Label {
6466 id: titleLabel;
6567 objectName: "titleLabel";
66− anchors { left: parent.left; right: parent.right }
68+ anchors { right: parent.right;
69+ rightMargin: units.gu(1);
70+ left: parent.left;
71+ top: parent.top; }
6772 elide: Text.ElideRight;
6873 fontSize: "small";
6974 wrapMode: Text.Wrap;
@@ -75,12 +80,15 @@ data: [ Label {
7580 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
7681 horizontalAlignment: root.headerAlignment;
7782 }
78−,
79−Label {
83+,Label {
8084 id: subtitleLabel;
8185 objectName: "subtitleLabel";
82− anchors { left: parent.left; right: parent.right }
83−
86+ anchors { right: parent.right;
87+ left: parent.left;
88+ rightMargin: units.gu(1);
89+ top: titleLabel.bottom;
90+ }
91+ anchors.topMargin: units.dp(2);
8492 elide: Text.ElideRight;
8593 fontSize: "small";
8694 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
tests/plugins/Dash/cardcreator/4.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"horizontal","card-size":"large","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1},"mascot":{"field":"mascot"},"subtitle":{"field":"domain"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1},"mascot":{"field":"mascot"},"subtitle":{"field":"domain"},"title":{"field":"title"},"attributes":{}}
33result: 4.res
tests/plugins/Dash/cardcreator/5.res+11−13
@@ -102,13 +102,12 @@ readonly property int headerHeight: titleLabel.height + subtitleLabel.height + s
102102Label {
103103 id: titleLabel;
104104 objectName: "titleLabel";
105− anchors { left: parent.left;
106− leftMargin: units.gu(1);
107− right: parent.right;
108− rightMargin: units.gu(1);
109− top: overlayLoader.top;
110− topMargin: units.gu(1);
111− }
105+ anchors { right: parent.right;
106+ left: parent.left;
107+ leftMargin: units.gu(1);
108+ top: overlayLoader.top;
109+ topMargin: units.gu(1);
110+ }
112111 elide: Text.ElideRight;
113112 fontSize: "small";
114113 wrapMode: Text.Wrap;
@@ -124,12 +123,11 @@ Label {
124123 id: subtitleLabel;
125124 objectName: "subtitleLabel";
126125 anchors { left: titleLabel.left;
127− leftMargin: titleLabel.leftMargin;
128− right: titleLabel.right;
129− rightMargin: titleLabel.rightMargin;
130− top: titleLabel.bottom;
131− topMargin: units.dp(2);
132− }
126+ leftMargin: titleLabel.leftMargin;
127+ right: titleLabel.right;
128+ top: titleLabel.bottom;
129+ }
130+ anchors.topMargin: units.dp(2);
133131 elide: Text.ElideRight;
134132 fontSize: "small";
135133 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
tests/plugins/Dash/cardcreator/5.tst+1−1
@@ -1,3 +1,3 @@
11template: {"card-layout":"vertical","card-size":"medium","category-layout":"carousel","collapsed-rows":2,"overlay":true}
2−components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"}}
2+components: {"art":{"aspect-ratio":1,"field":"art"},"subtitle":{"field":"artist"},"title":{"field":"title"},"attributes":{}}
33result: 5.res
tests/plugins/Dash/cardcreator/6.res+97−44
@@ -13,57 +13,110 @@ AbstractButton {
1313 property bool asynchronous: true;
1414 property bool showHeader: true;
1515 implicitWidth: childrenRect.width;
16−onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18−Item {
19− id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
24− id: artShapeLoader;
25− objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
27− asynchronous: root.asynchronous;
28− visible: status == Loader.Ready;
29− sourceComponent: UbuntuShape {
30− id: artShape;
31− objectName: "artShape";
32− radius: "medium";
33− visible: image.status == Image.Ready;
34− readonly property real fixedArtShapeSizeAspect: (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) ? root.fixedArtShapeSize.width / root.fixedArtShapeSize.height : -1;
35− readonly property real aspect: fixedArtShapeSizeAspect > 0 ? fixedArtShapeSizeAspect : components !== undefined ? components["art"]["aspect-ratio"] : 1;
36− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
37− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
38− function updateWidthHeightBindings() {
39− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
40− width = root.fixedArtShapeSize.width;
41− height = root.fixedArtShapeSize.height;
42− } else {
43− width = Qt.binding(function() { return !visible ? 0 : image.width });
44− height = Qt.binding(function() { return !visible ? 0 : image.height });
45− }
16+Loader {
17+ id: backgroundLoader;
18+ objectName: "backgroundLoader";
19+ anchors.fill: parent;
20+ asynchronous: root.asynchronous;
21+ visible: status == Loader.Ready;
22+ sourceComponent: UbuntuShape {
23+ objectName: "background";
24+ radius: "medium";
25+ color: getColor(0) || "white";
26+ gradientColor: getColor(1) || color;
27+ anchors.fill: parent;
28+ image: backgroundImage.source ? backgroundImage : null;
29+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
30+ property Image backgroundImage: Image {
31+ objectName: "backgroundImage";
32+ source: {
33+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
35+ else return "";
36+ }
37+ }
38+ function getColor(index) {
39+ if (cardData && typeof cardData["background"] === "object"
40+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
41+ return cardData["background"]["elements"][index];
42+ } else if (template && typeof template["card-background"] === "object"
43+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
44+ return template["card-background"]["elements"][index];
45+ } else return undefined;
46+ }
47+ }
4648 }
47− image: Image {
48− objectName: "artImage";
49− source: cardData && cardData["art"] || "";
50− cache: true;
51− asynchronous: root.asynchronous;
52− fillMode: Image.PreserveAspectCrop;
53− width: root.width;
54− height: width / artShape.aspect;
55− }
56− }
49+readonly property size artShapeSize: Qt.size(-1, -1);
50+readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
51+Item {
52+ id: headerTitleContainer;
53+ anchors { right: parent.right; left: parent.left;
54+ top: parent.top;
55+ topMargin: units.gu(1);
56+ leftMargin: units.gu(1);
57+ }
58+ width: parent.width - x;
59+ implicitHeight: titleLabel.height + subtitleLabel.height;
60+ data: [
61+ Label {
62+ id: titleLabel;
63+ objectName: "titleLabel";
64+ anchors { right: emblemIcon.left;
65+ rightMargin: emblemIcon.width > 0 ? units.gu(0.5) : 0;
66+ left: parent.left;
67+ top: parent.top; }
68+ elide: Text.ElideRight;
69+ fontSize: "small";
70+ wrapMode: Text.Wrap;
71+ maximumLineCount: 2;
72+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
73+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
74+ visible: showHeader ;
75+ text: root.title;
76+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
77+ horizontalAlignment: root.headerAlignment;
5778 }
58− }
59−readonly property int headerHeight: 0;
79+,Label {
80+ id: subtitleLabel;
81+ objectName: "subtitleLabel";
82+ anchors { right: parent.right;
83+ left: parent.left;
84+ rightMargin: units.gu(1);
85+ top: titleLabel.bottom;
86+ }
87+ anchors.topMargin: units.dp(2);
88+ elide: Text.ElideRight;
89+ fontSize: "small";
90+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
92+ visible: titleLabel.visible && titleLabel.text;
93+ text: cardData && cardData["subtitle"] || "";
94+ font.weight: Font.Light;
95+ horizontalAlignment: root.headerAlignment;
96+ }
97+,Icon {
98+ id: emblemIcon;
99+ objectName: "emblemIcon";
100+ anchors {
101+ bottom: titleLabel.baseline;
102+ right: parent.right;
103+ rightMargin: units.gu(1);
104+ }
105+ source: cardData && cardData["emblem"] || "";
106+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
107+ width: height;
108+ height: source != "" ? titleLabel.font.pixelSize : 0;
109+ }
110+
111+ ]
112+ }
60113UbuntuShape {
61114 id: touchdown;
62115 objectName: "touchdown";
63− anchors { fill: artShapeHolder }
116+ anchors { fill: backgroundLoader }
64117 visible: root.pressed;
65118 radius: "medium";
66119 borderSource: "radius_pressed.sci"
67120}
68−implicitHeight: artShapeHolder.height;
121+implicitHeight: headerTitleContainer.y + headerTitleContainer.height + units.gu(1);
69122}
tests/plugins/Dash/cardcreator/6.tst+2−2
@@ -1,3 +1,3 @@
1−template: {"card-layout":"vertical","card-size":"small","category-layout":"grid","collapsed-rows":2}
2−components: {"art":{"aspect-ratio":1.6,"field":"art"}}
1+template: {"card-background":{"elements":["#E9E9E9"],"type":"color"},"card-layout":"vertical","card-size":"medium","category-layout":"grid","collapsed-rows":2}
2+components: {"art":{"aspect-ratio":1},"background":{"field":"background"},"subtitle":{"field":"author"},"title":{"field":"title"},"emblem":{"field":"source"},"attributes":{}}
33result: 6.res
tests/plugins/Dash/cardcreator/7.res+32−20
@@ -55,17 +55,19 @@ Row {
5555 spacing: margins;
5656 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
5757 anchors { top: parent.top;
58− topMargin: units.gu(1);
59−left: parent.left;
60− }
58+ topMargin: units.gu(1);
59+ left: parent.left;
60+ }
6161 anchors.right: parent.right;
6262 anchors.margins: margins;
63− data: [ Image {
63+ anchors.rightMargin: 0;
64+ data: [
65+Image {
6466 id: mascotImage;
6567 objectName: "mascotImage";
6668 anchors { verticalCenter: parent.verticalCenter; }
6769 readonly property int maxSize: Math.max(width, height) * 4;
68− source: cardData && cardData["mascot"];
70+ source: cardData && cardData["mascot"] || "";
6971 width: units.gu(6);
7072 height: units.gu(5.625);
7173 sourceSize { width: maxSize; height: maxSize }
@@ -75,16 +77,19 @@ left: parent.left;
7577 visible: showHeader;
7678 }
7779
78− ,
79− Column {
80− anchors.verticalCenter: parent.verticalCenter;
81− spacing: units.dp(2);
82− width: parent.width - x;
83− data: [
84− Label {
80+,Item {
81+ id: headerTitleContainer;
82+ anchors { verticalCenter: parent.verticalCenter; }
83+ width: parent.width - x;
84+ implicitHeight: titleLabel.height + subtitleLabel.height + attributesRow.height;
85+ data: [
86+ Label {
8587 id: titleLabel;
8688 objectName: "titleLabel";
87− anchors { left: parent.left; right: parent.right }
89+ anchors { right: parent.right;
90+ rightMargin: units.gu(1);
91+ left: parent.left;
92+ top: parent.top; }
8893 elide: Text.ElideRight;
8994 fontSize: "small";
9095 wrapMode: Text.Wrap;
@@ -96,11 +101,15 @@ left: parent.left;
96101 font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
97102 horizontalAlignment: root.headerAlignment;
98103 }
99−,
100−Label {
104+,Label {
101105 id: subtitleLabel;
102106 objectName: "subtitleLabel";
103− anchors { left: parent.left; right: parent.right }
107+ anchors { right: parent.right;
108+ left: parent.left;
109+ rightMargin: units.gu(1);
110+ top: titleLabel.bottom;
111+ }
112+ anchors.topMargin: units.dp(2);
104113 elide: Text.ElideRight;
105114 fontSize: "small";
106115 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
@@ -110,13 +119,16 @@ Label {
110119 font.weight: Font.Light;
111120 horizontalAlignment: root.headerAlignment;
112121 }
113−,
114−CardAttributes {
122+,CardAttributes {
115123 id: attributesRow;
116124 objectName: "attributesRow";
117− anchors { left: parent.left; right: parent.right }
125+ anchors { right: parent.right;
126+ left: parent.left;
127+ rightMargin: units.gu(1);
128+ top: subtitleLabel.bottom;
129+ }
118130 color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < (root.scopeStyle ? root.scopeStyle.threshold : 0.7) ? (root.scopeStyle ? root.scopeStyle.light : "white") : (root.scopeStyle ? root.scopeStyle.dark : "grey");
119− model: cardData["attributes"];
131+ model: cardData && cardData["attributes"];
120132 }
121133
122134 ]
tests/qmltests/Dash/CardHelpers.js+1
@@ -60,6 +60,7 @@ var fullMapping = ' \
6060 "art": "art", \
6161 "subtitle": "subtitle", \
6262 "mascot": "mascot", \
63+ "emblem": "emblem", \
6364 "overlayColor": "overlayColor", \
6465 "summary": "summary", \
6566 "attributes": "attributes" \
tests/qmltests/Dash/artwork/emblem.pngnew file

Binary file not shown.

tests/qmltests/Dash/tst_Card.qml+19−1
@@ -31,6 +31,7 @@ Rectangle {
3131 {
3232 "art": "../../../tests/qmltests/Dash/artwork/music-player-design.png",
3333 "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
34+ "emblem": "../../../tests/qmltests/Dash/artwork/emblem.png",
3435 "title": "foo",
3536 "subtitle": "bar",
3637 "summary": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
@@ -270,7 +271,7 @@ Rectangle {
270271 { tag: "Large", width: units.gu(38), index: 2 },
271272 { tag: "Wide", height: units.gu(19), size: "large", index: 3 },
272273 { tag: "Tall", height: units.gu(38) / 0.7, size: "large", width: units.gu(38), index: 4 },
273− { tag: "VerticalWidth", width: function() { return headerRow.width + units.gu(1) * 2 }, index: 0 },
274+ { tag: "VerticalWidth", width: function() { return headerRow.width + units.gu(1) }, index: 0 },
274275 { tag: "HorizontalHeight", height: function() { return headerRow.height + units.gu(1) * 2 }, index: 5 },
275276 { tag: "HorizontalWidth", width: function() { return headerRow.x - units.gu(1) }, index: 5 },
276277 ]
@@ -458,6 +459,23 @@ Rectangle {
458459 tryCompareFunction(function() { return Qt.colorEqual(subtitle.color, fontColor); }, true);
459460 }
460461
462+ function test_emblemIcon_data() {
463+ return [
464+ { tag: "Art and summary", emblem: true, index: 0 },
465+ { tag: "Art and summary, small", emblem: false, index: 1 },
466+ { tag: "No header", emblem: false, index: 7 },
467+ { tag: "With background", emblem: true, index: 10 },
468+ ];
469+ }
470+
471+ function test_emblemIcon(data) {
472+ selector.selectedIndex = data.index;
473+ waitForRendering(card);
474+
475+ var emblemIcon = findChild(card, "emblemIcon");
476+ compare(emblemIcon !== null, data.emblem);
477+ }
478+
461479 function test_mascotShape_data() {
462480 return [
463481 { tag: "Art and summary", shape: false, index: 0 },