on xenial help sign in
Conflicts solved
2f50de2Andrea Cimitan
plugins/Dash/CardAttributes.qmlnew file+65
@@ -0,0 +1,65 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.2
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.1
20+import Ubuntu.Settings.Components 0.1
21+
22+GridLayout {
23+ id: grid
24+ anchors {
25+ left: parent.left;
26+ right: parent.right;
27+ }
28+ columns: 2 + repeater.count % 2
29+ property alias model: repeater.model
30+ property var color
31+
32+ Repeater {
33+ id: repeater
34+ delegate: Row {
35+ spacing: units.gu(0.5)
36+ readonly property int column: index % grid.columns;
37+ Layout.alignment: {
38+ if (column == 0) return Qt.AlignLeft;
39+ if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
40+ if (column == 1) return Qt.AlignHCenter;
41+ }
42+ Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
43+ Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
44+ Layout.fillWidth: true
45+ StatusIcon {
46+ id: icon
47+ height: units.gu(2)
48+ sets: ["actions", "status", "apps"]
49+ source: "icon" in modelData ? modelData["icon"] : ""
50+ }
51+ Label {
52+ id: label
53+ width: parent.width - x
54+ anchors.verticalCenter: parent.verticalCenter
55+ text: "value" in modelData ? modelData["value"] : "";
56+ elide: Text.ElideRight
57+ maximumLineCount: 1
58+ font.weight: "style" in modelData && modelData["style"] == "highlighted" ? Font.DemiBold : Font.Light
59+ fontSize: "small"
60+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale)
61+ color: grid.color
62+ }
63+ }
64+ }
65+}
plugins/Dash/CardCreator.js+136−103
@@ -142,10 +142,9 @@ var kOverlayLoaderCode = 'Loader { \n\
142 } \n\
142 } \n\
143 }\n';
143 }\n';
144
144
145−// %1 is used as anchors of row
145+// multiple row version of HeaderRowCode
146−// %2 is used as first child of the row
146+function kHeaderRowCodeGenerator() {
147−// %3 is used as second child of the row
147+var kHeaderRowCodeTemplate = 'Row { \n\
148−var kHeaderRow2Code = 'Row { \n\
149 id: row; \n\
148 id: row; \n\
150 objectName: "outerRow"; \n\
149 objectName: "outerRow"; \n\
151 property real margins: units.gu(1); \n\
150 property real margins: units.gu(1); \n\
@@ -153,35 +152,19 @@ var kHeaderRow2Code = 'Row { \n\
153 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; \n\
152 height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; \n\
154 anchors { %1 } \n\
153 anchors { %1 } \n\
155 anchors.right: parent.right; \n\
154 anchors.right: parent.right; \n\
156− anchors.margins: margins;\n\
155+ anchors.margins: margins; \n\
157− data: [ %2\n\
156+ anchors.rightMargin: units.gu(0); \n\
158− ,\n\
159− %3 \n\
160− ] \n\
161− }\n';
162−
163−// %1 is used as anchors of row
164−// %2 is used as first child of the row
165−// %3 is used as second child of the row
166−// %4 is used as third child of the row
167−var kHeaderRow3Code = 'Row { \n\
168− id: row; \n\
169− objectName: "outerRow"; \n\
170− property real margins: units.gu(1); \n\
171− spacing: margins; \n\
172− height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight; \n\
173− anchors { %1 } \n\
174− anchors.right: parent.right; \n\
175− anchors.margins: margins;\n\
176− data: [ %2\n\
157+ data: [ \n\
177− ,\n\
178− %3 \n\
179− ,\n\
180− %4 \n\
158+ %2 \n\
181 ] \n\
159 ] \n\
182 }\n';
160 }\n';
161+ var args = Array.prototype.slice.call(arguments);
162+ var HeaderRowAnchors = args.shift();
163+ var code = kHeaderRowCodeTemplate.arg(HeaderRowAnchors).arg(args.join(',\n'));
164+ return code;
165+}
183
166
184−// multiple column version of kHeaderContainerCode.
167+// multiple item version of kHeaderContainerCode
185function kHeaderContainerCodeGenerator() {
168function kHeaderContainerCodeGenerator() {
186 var headerContainerCodeTemplate = 'Item { \n\
169 var headerContainerCodeTemplate = 'Item { \n\
187 id: headerTitleContainer; \n\
170 id: headerTitleContainer; \n\
@@ -193,9 +176,9 @@ function kHeaderContainerCodeGenerator() {
193 ]\n\
176 ]\n\
194 }\n';
177 }\n';
195 var args = Array.prototype.slice.call(arguments);
178 var args = Array.prototype.slice.call(arguments);
196− var anchors = args.shift();
179+ var headerContainerAnchors = args.shift();
197− var height = args.shift();
180+ var headerContainerHeight = args.shift();
198− var code = headerContainerCodeTemplate.arg(anchors).arg(height).arg(args.join(',\n'));
181+ var code = headerContainerCodeTemplate.arg(headerContainerAnchors).arg(headerContainerHeight).arg(args.join(',\n'));
199 return code;
182 return code;
200}
183}
201
184
@@ -248,14 +231,14 @@ var kTitleLabelCode = 'Label { \n\
248 horizontalAlignment: root.headerAlignment; \n\
231 horizontalAlignment: root.headerAlignment; \n\
249 }\n';
232 }\n';
250
233
251−// %1 is used as additional anchors of emblem image
252var kEmblemImageCode = 'Image { \n\
234var kEmblemImageCode = 'Image { \n\
253 id: emblemImage; \n\
235 id: emblemImage; \n\
254 objectName: "emblemImage"; \n\
236 objectName: "emblemImage"; \n\
255 anchors { \n\
237 anchors { \n\
256 bottom: titleLabel.baseline; \n\
238 bottom: titleLabel.baseline; \n\
257 right: parent.right; \n\
239 right: parent.right; \n\
240+ rightMargin: units.gu(1); \n\
258− %1 } \n\
241+ } \n\
259 source: cardData && cardData["emblem"]; \n\
242 source: cardData && cardData["emblem"]; \n\
260 width: height; \n\
243 width: height; \n\
261 height: titleLabel.font.pixelSize; \n\
244 height: titleLabel.font.pixelSize; \n\
@@ -289,6 +272,15 @@ var kSubtitleLabelCode = 'Label { \n\
289 horizontalAlignment: root.headerAlignment; \n\
272 horizontalAlignment: root.headerAlignment; \n\
290 }\n';
273 }\n';
291
274
275+// %1 is used as anchors of attributesRow
276+var kAttributesRowCode = 'CardAttributes { \n\
277+ id: attributesRow; \n\
278+ objectName: "attributesRow"; \n\
279+ anchors { %1 } \n\
280+ color: %2; \n\
281+ model: cardData && cardData["attributes"] || undefined; \n\
282+ }\n';
283+
292// %1 is used as top anchor of summary
284// %1 is used as top anchor of summary
293// %2 is used as topMargin anchor of summary
285// %2 is used as topMargin anchor of summary
294// %3 is used as color of summary
286// %3 is used as color of summary
@@ -340,6 +332,7 @@ function cardString(template, components) {
340 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
332 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
341 var hasSubtitle = hasTitle && components["subtitle"] || false;
333 var hasSubtitle = hasTitle && components["subtitle"] || false;
342 var hasHeaderRow = hasMascot && hasTitle;
334 var hasHeaderRow = hasMascot && hasTitle;
335+ var hasAttributes = hasTitle && components["attributes"] || false;
343
336
344 if (hasBackground) {
337 if (hasBackground) {
345 code += kBackgroundLoaderCode;
338 code += kBackgroundLoaderCode;
@@ -350,9 +343,9 @@ function cardString(template, components) {
350 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
343 code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
351
344
352 var widthCode, heightCode;
345 var widthCode, heightCode;
353− var anchors;
346+ var artAnchors;
354 if (isHorizontal) {
347 if (isHorizontal) {
355− anchors = 'left: parent.left';
348+ artAnchors = 'left: parent.left';
356 if (hasMascot || hasTitle) {
349 if (hasMascot || hasTitle) {
357 widthCode = 'height * artShape.aspect'
350 widthCode = 'height * artShape.aspect'
358 heightCode = 'headerHeight + 2 * units.gu(1)';
351 heightCode = 'headerHeight + 2 * units.gu(1)';
@@ -363,12 +356,12 @@ function cardString(template, components) {
363 heightCode = 'units.gu(7.625)';
356 heightCode = 'units.gu(7.625)';
364 }
357 }
365 } else {
358 } else {
366− anchors = 'horizontalCenter: parent.horizontalCenter;';
359+ artAnchors = 'horizontalCenter: parent.horizontalCenter;';
367 widthCode = 'root.width'
360 widthCode = 'root.width'
368 heightCode = 'width / artShape.aspect';
361 heightCode = 'width / artShape.aspect';
369 }
362 }
370
363
371− code += kArtShapeHolderCode.arg(anchors).arg(widthCode).arg(heightCode);
364+ code += kArtShapeHolderCode.arg(artAnchors).arg(widthCode).arg(heightCode);
372 } else {
365 } else {
373 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
366 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
374 }
367 }
@@ -409,6 +402,14 @@ function cardString(template, components) {
409 code += 'readonly property int headerHeight: row.height;\n'
402 code += 'readonly property int headerHeight: row.height;\n'
410 } else if (hasMascot) {
403 } else if (hasMascot) {
411 code += 'readonly property int headerHeight: mascotImage.height;\n'
404 code += 'readonly property int headerHeight: mascotImage.height;\n'
405+ } else if (hasAttributes) {
406+ if (hasTitle && hasSubtitle) {
407+ code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + attributesRow.height + attributesRow.anchors.topMargin;\n'
408+ } else if (hasTitle) {
409+ code += 'readonly property int headerHeight: titleLabel.height + attributesRow.height + attributesRow.anchors.topMargin;\n'
410+ } else {
411+ code += 'readonly property int headerHeight: attributesRow.height;\n'
412+ }
412 } else if (hasSubtitle) {
413 } else if (hasSubtitle) {
413 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
414 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
414 } else if (hasTitle) {
415 } else if (hasTitle) {
@@ -417,49 +418,52 @@ function cardString(template, components) {
417 code += 'readonly property int headerHeight: 0;\n'
418 code += 'readonly property int headerHeight: 0;\n'
418 }
419 }
419
420
420− var mascotShapeCode = "";
421+ var mascotShapeCode = '';
421− var mascotCode = "";
422+ var mascotCode = '';
422 if (hasMascot) {
423 if (hasMascot) {
423 var useMascotShape = !hasBackground && !headerAsOverlay;
424 var useMascotShape = !hasBackground && !headerAsOverlay;
424− var anchors = "";
425+ var mascotAnchors = '';
425 if (!hasHeaderRow) {
426 if (!hasHeaderRow) {
426− anchors += headerLeftAnchor;
427+ mascotAnchors += headerLeftAnchor;
427− anchors += headerVerticalAnchors;
428+ mascotAnchors += headerVerticalAnchors;
428 if (!headerLeftAnchorHasMargin) {
429 if (!headerLeftAnchorHasMargin) {
429− anchors += 'leftMargin: units.gu(1);\n'
430+ mascotAnchors += 'leftMargin: units.gu(1);\n'
430 }
431 }
431 } else {
432 } else {
432− anchors = "verticalCenter: parent.verticalCenter;"
433+ mascotAnchors = 'verticalCenter: parent.verticalCenter;'
433 }
434 }
434
435
435 if (useMascotShape) {
436 if (useMascotShape) {
436− mascotShapeCode = kMascotShapeLoaderCode.arg(anchors);
437+ mascotShapeCode = kMascotShapeLoaderCode.arg(mascotAnchors);
437 }
438 }
438
439
439 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
440 var mascotImageVisible = useMascotShape ? 'false' : 'showHeader';
440− mascotCode = kMascotImageCode.arg(anchors).arg(mascotImageVisible);
441+ mascotCode = kMascotImageCode.arg(mascotAnchors).arg(mascotImageVisible);
441 }
442 }
442
443
443 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
444 var summaryColorWithBackground = 'backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey")';
444
445
445− var titleSubtitleCode = "";
446+ var titleSubtitleCode = '';
446 if (hasTitle) {
447 if (hasTitle) {
447− var color;
448+ var titleColor;
448 if (headerAsOverlay) {
449 if (headerAsOverlay) {
449− color = '"white"';
450+ titleColor = '"white"';
450 } else if (hasSummary) {
451 } else if (hasSummary) {
451− color = 'summary.color';
452+ titleColor = 'summary.color';
452 } else if (hasBackground) {
453 } else if (hasBackground) {
453− color = summaryColorWithBackground;
454+ titleColor = summaryColorWithBackground;
454 } else {
455 } else {
455− color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
456+ titleColor = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
456 }
457 }
457
458
458 var titleAnchors;
459 var titleAnchors;
459 var subtitleAnchors;
460 var subtitleAnchors;
461+ var attributesAnchors;
460 var titleRightAnchor;
462 var titleRightAnchor;
461 var titleContainerAnchors;
463 var titleContainerAnchors;
462
464
465+ // GOOD LUCK!
466+
463 if (hasMascot) {
467 if (hasMascot) {
464 titleContainerAnchors = 'verticalCenter: parent.verticalCenter; ';
468 titleContainerAnchors = 'verticalCenter: parent.verticalCenter; ';
465 } else {
469 } else {
@@ -476,14 +480,20 @@ function cardString(template, components) {
476 titleRightAnchor = 'right: parent.right;\n';
480 titleRightAnchor = 'right: parent.right;\n';
477 }
481 }
478
482
479− if (hasEmblem || (hasMascot && hasSubtitle)) {
483+ if (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes))) {
480− // Using titleContainer (and maybe row)
484+ // Using headerTitleContainer
481 titleAnchors = titleRightAnchor;
485 titleAnchors = titleRightAnchor;
482− titleAnchors += 'left: parent.left;\n\
486+ titleAnchors += 'left: parent.left; \n\
483− top: parent.top;';
487+ top: parent.top;';
484− subtitleAnchors = 'right: parent.right;\n\
488+ subtitleAnchors = 'right: parent.right; \n\
485− left: parent.left;\n\
489+ left: parent.left; \n\
490+ rightMargin: units.gu(1); \n';
491+ if (hasSubtitle) {
492+ attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
486− top: titleLabel.bottom;\n';
493+ subtitleAnchors += 'top: titleLabel.bottom;\n';
494+ } else {
495+ attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
496+ }
487 } else if (hasMascot) {
497 } else if (hasMascot) {
488 // Using row + titleContainer
498 // Using row + titleContainer
489 titleAnchors = 'verticalCenter: parent.verticalCenter;\n';
499 titleAnchors = 'verticalCenter: parent.verticalCenter;\n';
@@ -501,81 +511,100 @@ function cardString(template, components) {
501 titleAnchors += headerLeftAnchor;
511 titleAnchors += headerLeftAnchor;
502 titleAnchors += headerVerticalAnchors;
512 titleAnchors += headerVerticalAnchors;
503 if (!headerLeftAnchorHasMargin) {
513 if (!headerLeftAnchorHasMargin) {
504− titleAnchors += 'leftMargin: units.gu(1);\n'
514+ titleAnchors += 'leftMargin: units.gu(1);\n';
505 }
515 }
506 }
516 }
507 subtitleAnchors = 'left: titleLabel.left; \n\
517 subtitleAnchors = 'left: titleLabel.left; \n\
508 leftMargin: titleLabel.leftMargin; \n\
518 leftMargin: titleLabel.leftMargin; \n\
509− top: titleLabel.bottom;\n';
519+ rightMargin: units.gu(1); \n';
510 if (hasEmblem) {
520 if (hasEmblem) {
511 // using container
521 // using container
512 subtitleAnchors += 'right: parent.right; \n';
522 subtitleAnchors += 'right: parent.right; \n';
513 } else {
523 } else {
514 subtitleAnchors += 'right: titleLabel.right; \n';
524 subtitleAnchors += 'right: titleLabel.right; \n';
515 }
525 }
526+
527+ if (hasSubtitle) {
528+ attributesAnchors = subtitleAnchors + 'top: subtitleLabel.bottom;\n';
529+ subtitleAnchors += 'top: titleLabel.bottom;\n';
530+ } else {
531+ attributesAnchors = subtitleAnchors + 'top: titleLabel.bottom;\n';
532+ }
516 }
533 }
517
534
535+ // code for different elements
518 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
536 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
519− var titleCode;
520− titleCode = kTitleLabelCode.arg(titleAnchors).arg(color).arg(titleLabelVisibleExtra);
537+ var titleCode = kTitleLabelCode.arg(titleAnchors).arg(titleColor).arg(titleLabelVisibleExtra);
521− var subtitleCode = "";
538+ var subtitleCode;
539+ var attributesCode;
540+ var emblemCode;
541+
542+ // code for the title container
543+ var containerCode = [];
544+ var containerHeight = 'titleLabel.height';
545+ containerCode.push(titleCode);
522 if (hasSubtitle) {
546 if (hasSubtitle) {
523− subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
547+ subtitleCode = kSubtitleLabelCode.arg(subtitleAnchors).arg(titleColor);
548+ containerCode.push(subtitleCode);
549+ containerHeight += ' + subtitleLabel.height';
524 }
550 }
525−
551+ if (hasEmblem) {
526− var emblemCode;
552+ containerCode.push(kEmblemImageCode);
527− if (hasMascot) {
553+ }
528− emblemCode = kEmblemImageCode.arg('');
554+ if (hasAttributes) {
529− } else {
555+ attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(titleColor);
530− emblemCode = kEmblemImageCode.arg('rightMargin: units.gu(1);');
556+ containerCode.push(kAttributesRowCode.arg(attributesAnchors).arg(titleColor));
557+ containerHeight += ' + attributesRow.height';
531 }
558 }
532− if (hasEmblem && hasSubtitle) {
559+
533− // using container 3
534− titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, 'titleLabel.height + subtitleLabel.height', titleCode, emblemCode, kSubtitleLabelCode.arg(subtitleAnchors).arg(color));
535− } else if (hasEmblem) {
536− // container with emblem
537− titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, 'titleLabel.height', titleCode, emblemCode);
538− } else if (hasMascot && hasSubtitle) {
560+ if (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes))) {
539− // container with subtitle
561+ // use container
540− titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, 'titleLabel.height + subtitleLabel.height', titleCode, subtitleCode);
562+ titleSubtitleCode = kHeaderContainerCodeGenerator(titleContainerAnchors, containerHeight, containerCode);
541 } else {
563 } else {
542 // no container
564 // no container
543− titleSubtitleCode = titleCode + subtitleCode;
565+ titleSubtitleCode = titleCode;
566+ if (hasSubtitle) {
567+ titleSubtitleCode += subtitleCode;
568+ }
569+ if (hasAttributes) {
570+ titleSubtitleCode += attributesCode;
571+ }
544 }
572 }
545 }
573 }
546
574
547 if (hasHeaderRow) {
575 if (hasHeaderRow) {
576+ var rowCode = [mascotCode, titleSubtitleCode];
548− if (mascotShapeCode != "") {
577+ if (mascotShapeCode != '') {
549− code += kHeaderRow3Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotShapeCode).arg(mascotCode).arg(titleSubtitleCode);
578+ rowCode.unshift(mascotShapeCode);
550− } else {
551− code += kHeaderRow2Code.arg(headerVerticalAnchors + headerLeftAnchor).arg(mascotCode).arg(titleSubtitleCode);
552 }
579 }
580+ code += kHeaderRowCodeGenerator(headerVerticalAnchors + headerLeftAnchor, rowCode)
553 } else {
581 } else {
554 code += mascotShapeCode + mascotCode + titleSubtitleCode;
582 code += mascotShapeCode + mascotCode + titleSubtitleCode;
555 }
583 }
556
584
557 if (hasSummary) {
585 if (hasSummary) {
558 var summaryTopAnchor;
586 var summaryTopAnchor;
559− if (isHorizontal && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
587+ if (isHorizontal && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
560− else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
588+ else if (headerAsOverlay && hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
561− else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
589+ else if (hasHeaderRow) summaryTopAnchor = 'row.bottom';
562− else if (hasEmblem || (hasMascot && hasSubtitle)) summaryTopAnchor = "headerTitleContainer.bottom";
590+ else if (hasEmblem || (hasMascot && (hasSubtitle || hasAttributes))) summaryTopAnchor = 'headerTitleContainer.bottom';
563− else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
591+ else if (hasMascot) summaryTopAnchor = 'mascotImage.bottom';
592+ else if (hasAttributes) summaryTopAnchor = 'attributesRow.bottom';
564− else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
593+ else if (hasSubtitle) summaryTopAnchor = 'subtitleLabel.bottom';
565− else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
594+ else if (hasTitle) summaryTopAnchor = 'titleLabel.bottom';
566− else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
595+ else if (hasArt) summaryTopAnchor = 'artShapeHolder.bottom';
567− else summaryTopAnchor = "parent.top";
596+ else summaryTopAnchor = 'parent.top';
568−
597+
569− var color;
598+ var summaryColor;
570 if (hasBackground) {
599 if (hasBackground) {
571− color = summaryColorWithBackground;
600+ summaryColor = summaryColorWithBackground;
572 } else {
601 } else {
573− color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
602+ summaryColor = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
574 }
603 }
575
604
576− var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
605+ var summaryTopMargin = (hasMascot || hasSubtitle || hasAttributes ? 'anchors.margins' : '0');
577
606
578− code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
607+ code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(summaryColor);
579 }
608 }
580
609
581 var touchdownAnchors;
610 var touchdownAnchors;
@@ -594,6 +623,8 @@ function cardString(template, components) {
594 code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
623 code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
595 } else if (hasMascot) {
624 } else if (hasMascot) {
596 code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
625 code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
626+ } else if (hasAttributes) {
627+ code += 'implicitHeight: attributesRow.y + attributesRow.height + units.gu(1);\n';
597 } else if (hasSubtitle) {
628 } else if (hasSubtitle) {
598 code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
629 code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
599 } else if (hasTitle) {
630 } else if (hasTitle) {
@@ -610,8 +641,10 @@ function cardString(template, components) {
610function createCardComponent(parent, template, components) {
641function createCardComponent(parent, template, components) {
611 var imports = 'import QtQuick 2.2; \n\
642 var imports = 'import QtQuick 2.2; \n\
612 import Ubuntu.Components 0.1; \n\
643 import Ubuntu.Components 0.1; \n\
613− import Ubuntu.Thumbnailer 0.1;\n';
644+ import Ubuntu.Thumbnailer 0.1;\n\
645+ import Dash 0.1;\n';
614 var card = cardString(template, components);
646 var card = cardString(template, components);
615 var code = imports + 'Component {\n' + card + '}\n';
647 var code = imports + 'Component {\n' + card + '}\n';
648+
616 return Qt.createQmlObject(code, parent, "createCardComponent");
649 return Qt.createQmlObject(code, parent, "createCardComponent");
617}
650}
plugins/Dash/qmldir+1
@@ -3,3 +3,4 @@ plugin Dash-qml
3typeinfo Dash.qmltypes
3typeinfo Dash.qmltypes
4singleton CardCreatorCache 0.1 CardCreatorCache.qml
4singleton CardCreatorCache 0.1 CardCreatorCache.qml
5ScopeStyle 0.1 ScopeStyle.qml
5ScopeStyle 0.1 ScopeStyle.qml
6+CardAttributes 0.1 CardAttributes.qml
qml/Dash/CardTool.qml+27−2
@@ -165,15 +165,40 @@ Item {
165 }
165 }
166 }
166 }
167
167
168+ Item {
169+ id: attributesModel
170+ property int numOfAttributes: 0
171+ property var model: []
172+ property bool hasAttributes: {
173+ var attributes = components["attributes"];
174+ var hasAttributesFlag = (attributes != undefined) && attributes["field"];
175+
176+ if (hasAttributesFlag) {
177+ if (attributes["max-count"]) {
178+ numOfAttributes = attributes["max-count"];
179+ }
180+ }
181+ return hasAttributesFlag
182+ }
183+
184+ onNumOfAttributesChanged: {
185+ model = []
186+ for (var i = 0; i < numOfAttributes; i++) {
187+ model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
188+ }
189+ }
190+ }
191+
168 Loader {
192 Loader {
169 id: cardLoader
193 id: cardLoader
170− property var fields: ["art", "mascot", "title", "subtitle", "summary"]
194+ property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
171 property var maxData: {
195 property var maxData: {
172 "art": Qt.resolvedUrl("graphics/checkers.png"),
196 "art": Qt.resolvedUrl("graphics/checkers.png"),
173 "mascot": Qt.resolvedUrl("graphics/checkers.png"),
197 "mascot": Qt.resolvedUrl("graphics/checkers.png"),
174 "title": "—\n—",
198 "title": "—\n—",
175 "subtitle": "—",
199 "subtitle": "—",
176− "summary": "—\n—\n—\n—\n—"
200+ "summary": "—\n—\n—\n—\n—",
201+ "attributes": attributesModel.model
177 }
202 }
178 sourceComponent: cardTool.cardComponent
203 sourceComponent: cardTool.cardComponent
179 onLoaded: {
204 onLoaded: {
tests/plugins/Dash/CMakeLists.txt+1
@@ -79,3 +79,4 @@ set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
79set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
79set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
80add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
80add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
81add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
81add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
82+add_qml_test(. CardAttributes IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
tests/plugins/Dash/cardcreator/1.res+51−49
@@ -5,8 +5,8 @@ AbstractButton {
5 property var cardData;
5 property var cardData;
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8− property var scopeStyle: null;
8+ property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int headerAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -16,57 +16,59 @@ AbstractButton {
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
18Item {
19− id: artShapeHolder;
19+ id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
23+ Loader {
24− id: artShapeLoader;
24+ id: artShapeLoader;
25− objectName: "artShapeLoader";
25+ objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
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− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
56 asynchronous: root.asynchronous;
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+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
59+ width: root.width;
60− height: width / artShape.aspect;
60+ height: width / artShape.aspect;
61+ }
62+ }
61 }
63 }
62− }
64+ }
63− }
64− }
65readonly property int headerHeight: titleLabel.height;
65readonly property int headerHeight: titleLabel.height;
66Label {
66Label {
67− id: titleLabel;
67+ id: titleLabel;
68− objectName: "titleLabel";
68+ objectName: "titleLabel";
69− anchors { right: parent.right;left: parent.left;
69+ anchors { right: parent.right;
70+rightMargin: units.gu(1);
71+left: parent.left;
70top: artShapeHolder.bottom;
72top: artShapeHolder.bottom;
71 topMargin: units.gu(1);
73 topMargin: units.gu(1);
72leftMargin: units.gu(1);
74leftMargin: units.gu(1);
tests/plugins/Dash/cardcreator/2.res+94−87
@@ -5,8 +5,8 @@ AbstractButton {
5 property var cardData;
5 property var cardData;
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8− property var scopeStyle: null;
8+ property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int headerAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -14,105 +14,112 @@ AbstractButton {
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16Loader {
16Loader {
17− id: backgroundLoader;
17+ id: backgroundLoader;
18− objectName: "backgroundLoader";
18+ objectName: "backgroundLoader";
19− anchors.fill: parent;
19+ anchors.fill: parent;
20− asynchronous: root.asynchronous;
20+ asynchronous: root.asynchronous;
21− visible: status == Loader.Ready;
21+ visible: status == Loader.Ready;
22− sourceComponent: UbuntuShape {
22+ sourceComponent: UbuntuShape {
23− objectName: "background";
23+ objectName: "background";
24− radius: "medium";
24+ radius: "medium";
25− color: getColor(0) || "white";
25+ color: getColor(0) || "white";
26− gradientColor: getColor(1) || color;
26+ gradientColor: getColor(1) || color;
27− anchors.fill: parent;
27+ anchors.fill: parent;
28− image: backgroundImage.source ? backgroundImage : null;
28+ image: backgroundImage.source ? backgroundImage : null;
29− property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
29+ property real luminance: 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
30− property Image backgroundImage: Image {
30+ property Image backgroundImage: Image {
31− objectName: "backgroundImage";
31+ objectName: "backgroundImage";
32− source: {
32+ source: {
33− if (cardData && typeof cardData["background"] === "string") return cardData["background"];
33+ if (cardData && typeof cardData["background"] === "string") return cardData["background"];
34− else if (template && typeof template["card-background"] === "string") return template["card-background"];
34+ else if (template && typeof template["card-background"] === "string") return template["card-background"];
35− else return "";
35+ else return "";
36− }
36+ }
37− }
37+ }
38− function getColor(index) {
38+ function getColor(index) {
39− if (cardData && typeof cardData["background"] === "object"
39+ if (cardData && typeof cardData["background"] === "object"
40− && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
40+ && (cardData["background"]["type"] === "color" || cardData["background"]["type"] === "gradient")) {
41− return cardData["background"]["elements"][index];
41+ return cardData["background"]["elements"][index];
42− } else if (template && typeof template["card-background"] === "object"
42+ } else if (template && typeof template["card-background"] === "object"
43− && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
43+ && (template["card-background"]["type"] === "color" || template["card-background"]["type"] === "gradient")) {
44− return template["card-background"]["elements"][index];
44+ return template["card-background"]["elements"][index];
45− } else return undefined;
45+ } else return undefined;
46− }
46+ }
47− }
47+ }
48− }
48+ }
49readonly property size artShapeSize: Qt.size(-1, -1);
49readonly property size artShapeSize: Qt.size(-1, -1);
50readonly property int headerHeight: row.height;
50readonly property int headerHeight: row.height;
51Row {
51Row {
52− id: row;
52+ id: row;
53− objectName: "outerRow";
53+ objectName: "outerRow";
54− property real margins: units.gu(1);
54+ property real margins: units.gu(1);
55− spacing: margins;
55+ spacing: margins;
56− height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
56+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
57− anchors { top: parent.top;
57+ anchors { top: parent.top;
58 topMargin: units.gu(1);
58 topMargin: units.gu(1);
59− left: parent.left;
59+left: parent.left;
60−}
60+ }
61− anchors.right: parent.right;
61+ anchors.right: parent.right;
62− anchors.margins: margins;
62+ anchors.margins: margins;
63−data: [ Image {
63+ data: [ Image {
64− id: mascotImage;
64+ id: mascotImage;
65− objectName: "mascotImage";
65+ objectName: "mascotImage";
66− anchors { verticalCenter: parent.verticalCenter; }
66+ anchors { verticalCenter: parent.verticalCenter; }
67− readonly property int maxSize: Math.max(width, height) * 4;
67+ readonly property int maxSize: Math.max(width, height) * 4;
68− source: cardData && cardData["mascot"];
68+ source: cardData && cardData["mascot"];
69− width: units.gu(6);
69+ width: units.gu(6);
70− height: units.gu(5.625);
70+ height: units.gu(5.625);
71− sourceSize { width: maxSize; height: maxSize }
71+ sourceSize { width: maxSize; height: maxSize }
72− fillMode: Image.PreserveAspectCrop;
72+ fillMode: Image.PreserveAspectCrop;
73− horizontalAlignment: Image.AlignHCenter;
73+ horizontalAlignment: Image.AlignHCenter;
74− verticalAlignment: Image.AlignVCenter;
74+ verticalAlignment: Image.AlignVCenter;
75− visible: showHeader;
76− }
77−,
78−Column {
79− anchors.verticalCenter: parent.verticalCenter;
80− spacing: units.dp(2);
81− width: parent.width - x;
82− data: [ Label {
83− id: titleLabel;
84− objectName: "titleLabel";
85− anchors { left: parent.left; right: parent.right }
86− elide: Text.ElideRight;
87− fontSize: "small";
88− wrapMode: Text.Wrap;
89− maximumLineCount: 2;
90− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
92− visible: showHeader ;
75+ visible: showHeader;
93− text: root.title;
76+ }
94− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
95− horizontalAlignment: root.headerAlignment;
96− }
97−,
98−Label {
99− id: subtitleLabel;
100− objectName: "subtitleLabel";
101− anchors { left: parent.left; right: parent.right }
102
77
78+ ,
79+ Column {
80+ anchors.verticalCenter: parent.verticalCenter;
81+ spacing: units.dp(2);
82+ width: parent.width - x;
83+ data: [ Label {
84+ id: titleLabel;
85+ objectName: "titleLabel";
86+ anchors { left: parent.left; right: parent.right }
103 elide: Text.ElideRight;
87 elide: Text.ElideRight;
104 fontSize: "small";
88 fontSize: "small";
89+ wrapMode: Text.Wrap;
90+ maximumLineCount: 2;
105 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
91 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
106− color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
92+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
107− visible: titleLabel.visible && titleLabel.text;
93+ visible: showHeader ;
108− text: cardData && cardData["subtitle"] || "";
94+ text: root.title;
109− font.weight: Font.Light;
95+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
110 horizontalAlignment: root.headerAlignment;
96 horizontalAlignment: root.headerAlignment;
111 }
97 }
112]
98]
113}
99}
114]
100]
115}
101}
102+
103+ ,
104+ Label {
105+ id: subtitleLabel;
106+ objectName: "subtitleLabel";
107+ anchors { left: parent.left; right: parent.right }
108+ elide: Text.ElideRight;
109+ fontSize: "small";
110+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
111+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
112+ visible: titleLabel.visible && titleLabel.text;
113+ text: cardData && cardData["subtitle"] || "";
114+ font.weight: Font.Light;
115+ horizontalAlignment: root.headerAlignment;
116+ }
117+
118+ ]
119+ }
120+
121+ ]
122+ }
116UbuntuShape {
123UbuntuShape {
117 id: touchdown;
124 id: touchdown;
118 objectName: "touchdown";
125 objectName: "touchdown";
tests/plugins/Dash/cardcreator/3.res+77−74
@@ -5,8 +5,8 @@ AbstractButton {
5 property var cardData;
5 property var cardData;
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7 property real fontScale: 1.0;
7 property real fontScale: 1.0;
8− property var scopeStyle: null;
8+ property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int headerAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -16,90 +16,93 @@ AbstractButton {
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
18Item {
19− id: artShapeHolder;
19+ id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
23+ Loader {
24− id: artShapeLoader;
24+ id: artShapeLoader;
25− objectName: "artShapeLoader";
25+ objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
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− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
56 asynchronous: root.asynchronous;
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+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
59+ width: root.width;
60− height: width / artShape.aspect;
60+ height: width / artShape.aspect;
61+ }
62+ }
61 }
63 }
62− }
64+ }
63− }
64− }
65readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
65readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
66Label {
66Label {
67− id: titleLabel;
67+ id: titleLabel;
68− objectName: "titleLabel";
68+ objectName: "titleLabel";
69− anchors { right: parent.right;left: parent.left;
69+ anchors { right: parent.right;
70+rightMargin: units.gu(1);
71+left: parent.left;
70top: artShapeHolder.bottom;
72top: artShapeHolder.bottom;
71 topMargin: units.gu(1);
73 topMargin: units.gu(1);
72leftMargin: units.gu(1);
74leftMargin: units.gu(1);
73−}
74− elide: Text.ElideRight;
75− fontSize: "small";
76− wrapMode: Text.Wrap;
77− maximumLineCount: 2;
78− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
79− color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
80− visible: showHeader ;
81− text: root.title;
82− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
83− horizontalAlignment: root.headerAlignment;
84− }
75+ }
85−Label {
86− id: subtitleLabel;
87− objectName: "subtitleLabel";
88− anchors { left: titleLabel.left;
89− leftMargin: titleLabel.leftMargin;
90− right: titleLabel.right;
91− top: titleLabel.bottom;
92− topMargin: units.dp(2);
93− }
94 elide: Text.ElideRight;
76 elide: Text.ElideRight;
95 fontSize: "small";
77 fontSize: "small";
78+ wrapMode: Text.Wrap;
79+ maximumLineCount: 2;
96 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
80 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
97− color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
81+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
98− visible: titleLabel.visible && titleLabel.text;
82+ visible: showHeader ;
99− text: cardData && cardData["subtitle"] || "";
83+ text: root.title;
100− font.weight: Font.Light;
84+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
101 horizontalAlignment: root.headerAlignment;
85 horizontalAlignment: root.headerAlignment;
102 }
86 }
87+Label {
88+ id: subtitleLabel;
89+ objectName: "subtitleLabel";
90+ anchors { left: titleLabel.left;
91+ leftMargin: titleLabel.leftMargin;
92+ right: titleLabel.right;
93+ rightMargin: titleLabel.rightMargin;
94+ top: titleLabel.bottom;
95+ topMargin: units.dp(2);
96+ }
97+ elide: Text.ElideRight;
98+ fontSize: "small";
99+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
100+ color: root.scopeStyle ? root.scopeStyle.foreground : "grey";
101+ visible: titleLabel.visible && titleLabel.text;
102+ text: cardData && cardData["subtitle"] || "";
103+ font.weight: Font.Light;
104+ horizontalAlignment: root.headerAlignment;
105+ }
103UbuntuShape {
106UbuntuShape {
104 id: touchdown;
107 id: touchdown;
105 objectName: "touchdown";
108 objectName: "touchdown";
tests/plugins/Dash/cardcreator/5.res+119−117
@@ -4,9 +4,9 @@ AbstractButton {
4 property var components;
4 property var components;
5 property var cardData;
5 property var cardData;
6 property var artShapeBorderSource: undefined;
6 property var artShapeBorderSource: undefined;
7− property real fontScale: 1.0;
7+ property real fontScale: 1.0;
8− property var scopeStyle: null;
8+ property var scopeStyle: null;
9− property int headerAlignment: Text.AlignLeft;
9+ property int headerAlignment: Text.AlignLeft;
10 property int fixedHeaderHeight: -1;
10 property int fixedHeaderHeight: -1;
11 property size fixedArtShapeSize: Qt.size(-1, -1);
11 property size fixedArtShapeSize: Qt.size(-1, -1);
12 readonly property string title: cardData && cardData["title"] || "";
12 readonly property string title: cardData && cardData["title"] || "";
@@ -16,131 +16,133 @@ AbstractButton {
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
18Item {
19− id: artShapeHolder;
19+ id: artShapeHolder;
20− height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
20+ height: root.fixedArtShapeSize.height > 0 ? root.fixedArtShapeSize.height : artShapeLoader.height;
21− width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
21+ width: root.fixedArtShapeSize.width > 0 ? root.fixedArtShapeSize.width : artShapeLoader.width;
22− anchors { horizontalCenter: parent.horizontalCenter; }
22+ anchors { horizontalCenter: parent.horizontalCenter; }
23− Loader {
23+ Loader {
24− id: artShapeLoader;
24+ id: artShapeLoader;
25− objectName: "artShapeLoader";
25+ objectName: "artShapeLoader";
26− active: cardData && cardData["art"] || false;
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− readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37− Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38− onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39− Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40− function updateWidthHeightBindings() {
41− if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42− width = root.fixedArtShapeSize.width;
43− height = root.fixedArtShapeSize.height;
44− } else if (aspectSmallerThanImageAspect) {
45− width = Qt.binding(function() { return !visible ? 0 : image.width });
46− height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47− } else {
48− width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49− height = Qt.binding(function() { return !visible ? 0 : image.height });
50− }
51− }
52− image: Image {
53− objectName: "artImage";
54− source: cardData && cardData["art"] || "";
55− cache: true;
56 asynchronous: root.asynchronous;
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+ readonly property bool aspectSmallerThanImageAspect: aspect < image.aspect;
37+ Component.onCompleted: { updateWidthHeightBindings(); if (artShapeBorderSource !== undefined) borderSource = artShapeBorderSource; }
38+ onAspectSmallerThanImageAspectChanged: updateWidthHeightBindings();
39+ Connections { target: root; onFixedArtShapeSizeChanged: updateWidthHeightBindings(); }
40+ function updateWidthHeightBindings() {
41+ if (root.fixedArtShapeSize.height > 0 && root.fixedArtShapeSize.width > 0) {
42+ width = root.fixedArtShapeSize.width;
43+ height = root.fixedArtShapeSize.height;
44+ } else if (aspectSmallerThanImageAspect) {
45+ width = Qt.binding(function() { return !visible ? 0 : image.width });
46+ height = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.height : width / image.aspect });
47+ } else {
48+ width = Qt.binding(function() { return !visible ? 0 : image.fillMode === Image.PreserveAspectCrop ? image.width : height * image.aspect });
49+ height = Qt.binding(function() { return !visible ? 0 : image.height });
50+ }
51+ }
52+ image: Image {
53+ objectName: "artImage";
54+ source: cardData && cardData["art"] || "";
55+ cache: true;
56+ asynchronous: root.asynchronous;
57− fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
57+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop;
58− readonly property real aspect: implicitWidth / implicitHeight;
58+ readonly property real aspect: implicitWidth / implicitHeight;
59− width: root.width;
59+ width: root.width;
60− height: width / artShape.aspect;
60+ height: width / artShape.aspect;
61+ }
62+ }
61 }
63 }
62− }
64+ }
63− }
64− }
65Loader {
65Loader {
66− id: overlayLoader;
66+ id: overlayLoader;
67− anchors {
67+ anchors {
68− left: artShapeHolder.left;
68+ left: artShapeHolder.left;
69− right: artShapeHolder.right;
69+ right: artShapeHolder.right;
70− bottom: artShapeHolder.bottom;
70+ bottom: artShapeHolder.bottom;
71− }
71+ }
72− active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
72+ active: artShapeLoader.active && artShapeLoader.item && artShapeLoader.item.image.status === Image.Ready || false;
73− asynchronous: root.asynchronous;
73+ asynchronous: root.asynchronous;
74− visible: showHeader && status == Loader.Ready;
74+ visible: showHeader && status == Loader.Ready;
75− sourceComponent: ShaderEffect {
75+ sourceComponent: ShaderEffect {
76− id: overlay;
76+ id: overlay;
77− height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
77+ height: (fixedHeaderHeight > 0 ? fixedHeaderHeight : headerHeight) + units.gu(2);
78− opacity: 0.6;
78+ opacity: 0.6;
79− property var source: ShaderEffectSource {
79+ property var source: ShaderEffectSource {
80− id: shaderSource;
80+ id: shaderSource;
81− sourceItem: artShapeLoader.item;
81+ sourceItem: artShapeLoader.item;
82− onVisibleChanged: if (visible) scheduleUpdate();
82+ onVisibleChanged: if (visible) scheduleUpdate();
83− live: false;
83+ live: false;
84− sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
84+ sourceRect: Qt.rect(0, artShapeLoader.height - overlay.height, artShapeLoader.width, overlay.height);
85− }
85+ }
86− vertexShader: "
86+ vertexShader: "
87− uniform highp mat4 qt_Matrix;
87+ uniform highp mat4 qt_Matrix;
88− attribute highp vec4 qt_Vertex;
88+ attribute highp vec4 qt_Vertex;
89− attribute highp vec2 qt_MultiTexCoord0;
89+ attribute highp vec2 qt_MultiTexCoord0;
90− varying highp vec2 coord;
90+ varying highp vec2 coord;
91− void main() {
91+ void main() {
92− coord = qt_MultiTexCoord0;
92+ coord = qt_MultiTexCoord0;
93− gl_Position = qt_Matrix * qt_Vertex;
93+ gl_Position = qt_Matrix * qt_Vertex;
94− }";
94+ }";
95− fragmentShader: "
95+ fragmentShader: "
96− varying highp vec2 coord;
96+ varying highp vec2 coord;
97− uniform sampler2D source;
97+ uniform sampler2D source;
98− uniform lowp float qt_Opacity;
98+ uniform lowp float qt_Opacity;
99− void main() {
99+ void main() {
100− lowp vec4 tex = texture2D(source, coord);
100+ lowp vec4 tex = texture2D(source, coord);
101− gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
101+ gl_FragColor = vec4(0, 0, 0, tex.a) * qt_Opacity;
102− }";
102+ }";
103− }
103+ }
104− }
104+ }
105readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
105readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
106Label {
106Label {
107− id: titleLabel;
107+ id: titleLabel;
108− objectName: "titleLabel";
108+ objectName: "titleLabel";
109− anchors { left: parent.left;
109+ anchors { left: parent.left;
110− leftMargin: units.gu(1);
110+ leftMargin: units.gu(1);
111− right: parent.right;
111+ right: parent.right;
112− top: overlayLoader.top;
113− topMargin: units.gu(1);
112+ rightMargin: units.gu(1);
114−}
115− elide: Text.ElideRight;
116− fontSize: "small";
117− wrapMode: Text.Wrap;
118− maximumLineCount: 2;
119− font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
120− color: "white";
121− visible: showHeader && overlayLoader.active;
113+ top: overlayLoader.top;
122− text: root.title;
123− font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
124− horizontalAlignment: root.headerAlignment;
125− }
126−Label {
127− id: subtitleLabel;
128− objectName: "subtitleLabel";
129− anchors { left: titleLabel.left;
130− leftMargin: titleLabel.leftMargin;
131− right: titleLabel.right;
132− top: titleLabel.bottom;
133− topMargin: units.dp(2);
114+ topMargin: units.gu(1);
134− }
115+ }
135 elide: Text.ElideRight;
116 elide: Text.ElideRight;
136 fontSize: "small";
117 fontSize: "small";
118+ wrapMode: Text.Wrap;
119+ maximumLineCount: 2;
137 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
120 font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
138 color: "white";
121 color: "white";
139− visible: titleLabel.visible && titleLabel.text;
122+ visible: showHeader && overlayLoader.active;
140− text: cardData && cardData["subtitle"] || "";
123+ text: root.title;
141− font.weight: Font.Light;
124+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
142 horizontalAlignment: root.headerAlignment;
125 horizontalAlignment: root.headerAlignment;
143 }
126 }
127+Label {
128+ id: subtitleLabel;
129+ objectName: "subtitleLabel";
130+ anchors { left: titleLabel.left;
131+ leftMargin: titleLabel.leftMargin;
132+ right: titleLabel.right;
133+ rightMargin: titleLabel.rightMargin;
134+ top: titleLabel.bottom;
135+ topMargin: units.dp(2);
136+ }
137+ elide: Text.ElideRight;
138+ fontSize: "small";
139+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
140+ color: "white";
141+ visible: titleLabel.visible && titleLabel.text;
142+ text: cardData && cardData["subtitle"] || "";
143+ font.weight: Font.Light;
144+ horizontalAlignment: root.headerAlignment;
145+ }
144UbuntuShape {
146UbuntuShape {
145 id: touchdown;
147 id: touchdown;
146 objectName: "touchdown";
148 objectName: "touchdown";
tests/plugins/Dash/cardcreator/7.resnew file+128
@@ -0,0 +1,128 @@
1+AbstractButton {
2+ id: root;
3+ property var template;
4+ property var components;
5+ property var cardData;
6+ property var artShapeBorderSource: undefined;
7+ property real fontScale: 1.0;
8+ property var scopeStyle: null;
9+ property int headerAlignment: Text.AlignLeft;
10+ property int fixedHeaderHeight: -1;
11+ property size fixedArtShapeSize: Qt.size(-1, -1);
12+ readonly property string title: cardData && cardData["title"] || "";
13+ property bool asynchronous: true;
14+ property bool showHeader: true;
15+ implicitWidth: childrenRect.width;
16+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+ }
48+ }
49+readonly property size artShapeSize: Qt.size(-1, -1);
50+readonly property int headerHeight: row.height;
51+Row {
52+ id: row;
53+ objectName: "outerRow";
54+ property real margins: units.gu(1);
55+ spacing: margins;
56+ height: root.fixedHeaderHeight != -1 ? root.fixedHeaderHeight : implicitHeight;
57+ anchors { top: parent.top;
58+ topMargin: units.gu(1);
59+left: parent.left;
60+ }
61+ anchors.right: parent.right;
62+ anchors.margins: margins;
63+ data: [ Image {
64+ id: mascotImage;
65+ objectName: "mascotImage";
66+ anchors { verticalCenter: parent.verticalCenter; }
67+ readonly property int maxSize: Math.max(width, height) * 4;
68+ source: cardData && cardData["mascot"];
69+ width: units.gu(6);
70+ height: units.gu(5.625);
71+ sourceSize { width: maxSize; height: maxSize }
72+ fillMode: Image.PreserveAspectCrop;
73+ horizontalAlignment: Image.AlignHCenter;
74+ verticalAlignment: Image.AlignVCenter;
75+ visible: showHeader;
76+ }
77+
78+ ,
79+ Column {
80+ anchors.verticalCenter: parent.verticalCenter;
81+ spacing: units.dp(2);
82+ width: parent.width - x;
83+ data: [
84+ Label {
85+ id: titleLabel;
86+ objectName: "titleLabel";
87+ anchors { left: parent.left; right: parent.right }
88+ elide: Text.ElideRight;
89+ fontSize: "small";
90+ wrapMode: Text.Wrap;
91+ maximumLineCount: 2;
92+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
93+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
94+ visible: showHeader ;
95+ text: root.title;
96+ font.weight: components && components["subtitle"] ? Font.DemiBold : Font.Normal;
97+ horizontalAlignment: root.headerAlignment;
98+ }
99+,
100+Label {
101+ id: subtitleLabel;
102+ objectName: "subtitleLabel";
103+ anchors { left: parent.left; right: parent.right }
104+ elide: Text.ElideRight;
105+ fontSize: "small";
106+ font.pixelSize: Math.round(FontUtils.sizeToPixels(fontSize) * fontScale);
107+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
108+ visible: titleLabel.visible && titleLabel.text;
109+ text: cardData && cardData["subtitle"] || "";
110+ font.weight: Font.Light;
111+ horizontalAlignment: root.headerAlignment;
112+ }
113+,
114+CardAttributes {
115+ id: attributesRow;
116+ objectName: "attributesRow";
117+ anchors { left: parent.left; right: parent.right }
118+ color: backgroundLoader.active && backgroundLoader.item && backgroundLoader.item.luminance < 0.7 ? "white" : (root.scopeStyle ? root.scopeStyle.foreground : "grey");
119+ model: cardData["attributes"]
120+ }
121+
122+ ]
123+ }
124+
125+ ]
126+ }
127+implicitHeight: row.y + row.height + units.gu(1);
128+}
tests/plugins/Dash/cardcreator/7.tstnew file+3
@@ -0,0 +1,3 @@
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,"fill-mode":"crop"},"background":{"field":"background"},"mascot":{"field":"icon"},"subtitle":{"field":"author"},"title":{"field":"title"},"attributes":{"field":"attributes","max-count":2}}
3+result: 7.res
tests/plugins/Dash/cardcreatortest.cpp+10
@@ -20,6 +20,7 @@
20#include <QQuickView>
20#include <QQuickView>
21#include <QtTestGui>
21#include <QtTestGui>
22#include <QDebug>
22#include <QDebug>
23+#include <QTemporaryFile>
23
24
24class CardCreatorTest : public QObject
25class CardCreatorTest : public QObject
25{
26{
@@ -74,12 +75,21 @@ private Q_SLOTS:
74 QFile testResultFile(testDirPath + resultFileName);
75 QFile testResultFile(testDirPath + resultFileName);
75 QVERIFY(testResultFile.open(QIODevice::ReadOnly));
76 QVERIFY(testResultFile.open(QIODevice::ReadOnly));
76 QTextStream ts2(&testResultFile);
77 QTextStream ts2(&testResultFile);
78+
79+ // Record failed results to /tmp
80+ const QString executedResult = cardStringResult.toString();
81+ QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "testCardCreatorFailedResultXXXXXX");
82+ tmpFile.open();
83+ tmpFile.setAutoRemove(false);
84+ tmpFile.write(executedResult.toUtf8().constData());
77 const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
85 const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
78 const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
86 const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
79 for (int i = 0; i < expectedLines.size(); ++i) {
87 for (int i = 0; i < expectedLines.size(); ++i) {
80 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
88 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
81 }
89 }
82
90
91+ tmpFile.setAutoRemove(true); // Remove the result if it passed
92+
83 QVariant createCardComponentResult;
93 QVariant createCardComponentResult;
84 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
94 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
85 QVERIFY(createCardComponentResult.toBool());
95 QVERIFY(createCardComponentResult.toBool());
tests/plugins/Dash/tst_CardAttributes.qmlnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright 2013 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import Dash 0.1
20+
21+Item {
22+ width: units.gu(40)
23+ height: units.gu(4.5)
24+
25+ property var testData: [
26+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}],
27+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3"}],
28+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3"},{"value":"text4"}],
29+ [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"},{"value":"text3","style":"highlighted"},{"value":"text4","icon":"image://theme/close","style":"highlighted"},{"value":"text5"}]
30+ ]
31+
32+ CardAttributes {
33+ id: cardAttributes
34+ model: testData[3]
35+ clip: true
36+ }
37+
38+ TestCase {
39+ name: "CardAttributesTest"
40+ when: windowShown
41+
42+ function init() {
43+ }
44+
45+ function test_columns_data() {
46+ return testData;
47+ }
48+
49+ function test_columns(data) {
50+ cardAttributes.model = data;
51+ compare(cardAttributes.columns, 2 + data.length % 2);
52+ }
53+ }
54+}
tests/qmltests/Dash/CardHelpers.js+5−3
@@ -16,7 +16,7 @@
16
16
17.pragma library
17.pragma library
18
18
19−var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary"]
19+var components = ["title", "art", "subtitle", "mascot", "emblem", "old-price", "price", "alt-price", "rating", "alt-rating", "summary", "attributes"]
20
20
21var defaultLayout = ' \
21var defaultLayout = ' \
22{ \
22{ \
@@ -48,7 +48,8 @@ var defaultLayout = ' \
48 "empty": "image://theme/rating-star-empty" \
48 "empty": "image://theme/rating-star-empty" \
49 }, \
49 }, \
50 "alt-rating": null, \
50 "alt-rating": null, \
51− "summary": null \
51+ "summary": null, \
52+ "attributes": { "max-count": 2 } \
52 }, \
53 }, \
53 "resources": {} \
54 "resources": {} \
54}'
55}'
@@ -60,7 +61,8 @@ var fullMapping = ' \
60 "subtitle": "subtitle", \
61 "subtitle": "subtitle", \
61 "mascot": "mascot", \
62 "mascot": "mascot", \
62 "emblem": "emblem", \
63 "emblem": "emblem", \
63− "summary": "summary" \
64+ "summary": "summary", \
65+ "attributes": "attributes" \
64}'
66}'
65
67
66
68
tests/qmltests/Dash/tst_Card.qml+3−1
@@ -34,7 +34,8 @@ Rectangle {
34 "emblem": "../../../tests/qmltests/Dash/artwork/emblem.png",
34 "emblem": "../../../tests/qmltests/Dash/artwork/emblem.png",
35 "title": "foo",
35 "title": "foo",
36 "subtitle": "bar",
36 "subtitle": "bar",
37− "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."
37+ "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.",
38+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
38 }'
39 }'
39
40
40 property var cardsModel: [
41 property var cardsModel: [
@@ -111,6 +112,7 @@ Rectangle {
111 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
112 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
112
113
113 sourceComponent: cardTool.cardComponent
114 sourceComponent: cardTool.cardComponent
115+ clip: true
114 onLoaded: {
116 onLoaded: {
115 item.template = Qt.binding(function() { return cardTool.template; });
117 item.template = Qt.binding(function() { return cardTool.template; });
116 item.components = Qt.binding(function() { return cardTool.components; });
118 item.components = Qt.binding(function() { return cardTool.components; });