on xenial help sign in
Merge attribute branch and add improved tests
5c246d4Andrea 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+75−6
@@ -193,6 +193,21 @@ var kHeaderColumnCode = 'Column { \n\
193 ] \n\
193 ] \n\
194 }\n';
194 }\n';
195
195
196+// multiple column version of kHeaderColumnCode.
197+function kHeaderColumnCodeGenerator() {
198+ var headerColumnCodeTemplate = 'Column { \n\
199+ anchors.verticalCenter: parent.verticalCenter; \n\
200+ spacing: units.dp(2); \n\
201+ width: parent.width - x;\n\
202+ data: [ \n\
203+ %1 \n\
204+ ]\n\
205+ }\n';
206+ var args = Array.prototype.slice.call(arguments);
207+ var code = headerColumnCodeTemplate.arg(args.join(',\n'));
208+ return code;
209+}
210+
196// %1 is used as anchors of mascotShapeLoader
211// %1 is used as anchors of mascotShapeLoader
197var kMascotShapeLoaderCode = 'Loader { \n\
212var kMascotShapeLoaderCode = 'Loader { \n\
198 id: mascotShapeLoader; \n\
213 id: mascotShapeLoader; \n\
@@ -268,6 +283,15 @@ var kSubtitleLabelCode = 'Label { \n\
268 horizontalAlignment: root.headerAlignment; \n\
283 horizontalAlignment: root.headerAlignment; \n\
269 }\n';
284 }\n';
270
285
286+// %1 is used as anchors of attributesRow
287+var kAttributesRowCode = 'CardAttributes { \n\
288+ id: attributesRow; \n\
289+ objectName: "attributesRow"; \n\
290+ anchors { %1 } \n\
291+ color: %2; \n\
292+ model: cardData["attributes"]; \n\
293+ }\n';
294+
271// %1 is used as top anchor of summary
295// %1 is used as top anchor of summary
272// %2 is used as topMargin anchor of summary
296// %2 is used as topMargin anchor of summary
273// %3 is used as color of summary
297// %3 is used as color of summary
@@ -318,6 +342,7 @@ function cardString(template, components) {
318 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
342 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
319 var hasSubtitle = hasTitle && components["subtitle"] || false;
343 var hasSubtitle = hasTitle && components["subtitle"] || false;
320 var hasHeaderRow = hasMascot && hasTitle;
344 var hasHeaderRow = hasMascot && hasTitle;
345+ var hasAttributes = hasTitle && components["attributes"] || false;
321
346
322 if (hasBackground) {
347 if (hasBackground) {
323 code += kBackgroundLoaderCode;
348 code += kBackgroundLoaderCode;
@@ -387,6 +412,14 @@ function cardString(template, components) {
387 code += 'readonly property int headerHeight: row.height;\n'
412 code += 'readonly property int headerHeight: row.height;\n'
388 } else if (hasMascot) {
413 } else if (hasMascot) {
389 code += 'readonly property int headerHeight: mascotImage.height;\n'
414 code += 'readonly property int headerHeight: mascotImage.height;\n'
415+ } else if (hasAttributes) {
416+ if (hasTitle && hasSubtitle) {
417+ code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + attributesRow.height + attributesRow.anchors.topMargin;\n'
418+ } else if (hasTitle) {
419+ code += 'readonly property int headerHeight: titleLabel.height + attributesRow.height + attributesRow.anchors.topMargin;\n'
420+ } else {
421+ code += 'readonly property int headerHeight: attributesRow.height;\n'
422+ }
390 } else if (hasSubtitle) {
423 } else if (hasSubtitle) {
391 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
424 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
392 } else if (hasTitle) {
425 } else if (hasTitle) {
@@ -435,10 +468,12 @@ function cardString(template, components) {
435
468
436 var titleAnchors;
469 var titleAnchors;
437 var subtitleAnchors;
470 var subtitleAnchors;
471+ var attributesAnchors;
438− if (hasMascot && hasSubtitle) {
472+ if (hasMascot && (hasSubtitle || hasAttributes)) {
439 // Using row + column
473 // Using row + column
440 titleAnchors = 'left: parent.left; right: parent.right';
474 titleAnchors = 'left: parent.left; right: parent.right';
441 subtitleAnchors = titleAnchors;
475 subtitleAnchors = titleAnchors;
476+ attributesAnchors = subtitleAnchors;
442 } else if (hasMascot) {
477 } else if (hasMascot) {
443 // Using row + label
478 // Using row + label
444 titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
479 titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
@@ -448,11 +483,13 @@ function cardString(template, components) {
448 titleAnchors = 'left: parent.left; \n\
483 titleAnchors = 'left: parent.left; \n\
449 leftMargin: units.gu(1); \n\
484 leftMargin: units.gu(1); \n\
450 right: parent.right; \n\
485 right: parent.right; \n\
486+ rightMargin: units.gu(1); \n\
451 top: overlayLoader.top; \n\
487 top: overlayLoader.top; \n\
452 topMargin: units.gu(1);\n';
488 topMargin: units.gu(1);\n';
453 } else {
489 } else {
454 // Using anchors to the mascot/parent
490 // Using anchors to the mascot/parent
455− titleAnchors = "right: parent.right;";
491+ titleAnchors = "right: parent.right;\n";
492+ titleAnchors += "rightMargin: units.gu(1);\n";
456 titleAnchors += headerLeftAnchor;
493 titleAnchors += headerLeftAnchor;
457 titleAnchors += headerVerticalAnchors;
494 titleAnchors += headerVerticalAnchors;
458 if (!headerLeftAnchorHasMargin) {
495 if (!headerLeftAnchorHasMargin) {
@@ -462,8 +499,19 @@ function cardString(template, components) {
462 subtitleAnchors = 'left: titleLabel.left; \n\
499 subtitleAnchors = 'left: titleLabel.left; \n\
463 leftMargin: titleLabel.leftMargin; \n\
500 leftMargin: titleLabel.leftMargin; \n\
464 right: titleLabel.right; \n\
501 right: titleLabel.right; \n\
502+ rightMargin: titleLabel.rightMargin; \n\
465 top: titleLabel.bottom; \n\
503 top: titleLabel.bottom; \n\
466 topMargin: units.dp(2);\n';
504 topMargin: units.dp(2);\n';
505+ if (hasSubtitle) {
506+ attributesAnchors = 'left: subtitleLabel.left; \n\
507+ leftMargin: subtitleLabel.leftMargin; \n\
508+ right: subtitleLabel.right; \n\
509+ rightMargin: subtitleLabel.rightMargin; \n\
510+ top: subtitleLabel.bottom; \n\
511+ topMargin: units.dp(2);\n';
512+ } else {
513+ attributesAnchors = subtitleAnchors;
514+ }
467 }
515 }
468
516
469 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
517 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
@@ -473,11 +521,27 @@ function cardString(template, components) {
473 subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
521 subtitleCode += kSubtitleLabelCode.arg(subtitleAnchors).arg(color);
474 }
522 }
475
523
476− if (hasMascot && hasSubtitle) {
524+ if (hasMascot && (hasSubtitle || hasAttributes)) {
477 // If using row + column wrap the code in the column
525 // If using row + column wrap the code in the column
478 titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
526 titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
527+ if (hasSubtitle && hasAttributes) {
528+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
529+ titleSubtitleCode = kHeaderColumnCodeGenerator(titleCode, subtitleCode, attributesCode);
530+ } else if (hasSubtitle) {
531+ titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
532+ } else if (hasAttributes) {
533+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
534+ titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(attributesCode);
535+ }
479 } else {
536 } else {
480− titleSubtitleCode = titleCode + subtitleCode;
537+ titleSubtitleCode = titleCode;
538+ if (hasSubtitle) {
539+ titleSubtitleCode = titleSubtitleCode + subtitleCode;
540+ }
541+ if (hasAttributes) {
542+ var attributesCode = kAttributesRowCode.arg(attributesAnchors).arg(color);
543+ titleSubtitleCode = titleSubtitleCode + attributesCode;
544+ }
481 }
545 }
482 }
546 }
483
547
@@ -497,6 +561,7 @@ function cardString(template, components) {
497 else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
561 else if (headerAsOverlay && hasArt) summaryTopAnchor = "artShapeHolder.bottom";
498 else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
562 else if (hasHeaderRow) summaryTopAnchor = "row.bottom";
499 else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
563 else if (hasMascot) summaryTopAnchor = "mascotImage.bottom";
564+ else if (hasAttributes) summaryTopAnchor = "attributesRow.bottom";
500 else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
565 else if (hasSubtitle) summaryTopAnchor = "subtitleLabel.bottom";
501 else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
566 else if (hasTitle) summaryTopAnchor = "titleLabel.bottom";
502 else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
567 else if (hasArt) summaryTopAnchor = "artShapeHolder.bottom";
@@ -509,7 +574,7 @@ function cardString(template, components) {
509 color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
574 color = 'root.scopeStyle ? root.scopeStyle.foreground : "grey"';
510 }
575 }
511
576
512− var summaryTopMargin = (hasMascot || hasSubtitle ? 'anchors.margins' : '0');
577+ var summaryTopMargin = (hasMascot || hasSubtitle || hasAttributes ? 'anchors.margins' : '0');
513
578
514 code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
579 code += kSummaryLabelCode.arg(summaryTopAnchor).arg(summaryTopMargin).arg(color);
515 }
580 }
@@ -530,6 +595,8 @@ function cardString(template, components) {
530 code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
595 code += 'implicitHeight: row.y + row.height + units.gu(1);\n';
531 } else if (hasMascot) {
596 } else if (hasMascot) {
532 code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
597 code += 'implicitHeight: mascotImage.y + mascotImage.height;\n';
598+ } else if (hasAttributes) {
599+ code += 'implicitHeight: attributesRow.y + attributesRow.height + units.gu(1);\n';
533 } else if (hasSubtitle) {
600 } else if (hasSubtitle) {
534 code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
601 code += 'implicitHeight: subtitleLabel.y + subtitleLabel.height + units.gu(1);\n';
535 } else if (hasTitle) {
602 } else if (hasTitle) {
@@ -546,8 +613,10 @@ function cardString(template, components) {
546function createCardComponent(parent, template, components) {
613function createCardComponent(parent, template, components) {
547 var imports = 'import QtQuick 2.2; \n\
614 var imports = 'import QtQuick 2.2; \n\
548 import Ubuntu.Components 0.1; \n\
615 import Ubuntu.Components 0.1; \n\
549− import Ubuntu.Thumbnailer 0.1;\n';
616+ import Ubuntu.Thumbnailer 0.1;\n\
617+ import Dash 0.1;\n';
550 var card = cardString(template, components);
618 var card = cardString(template, components);
551 var code = imports + 'Component {\n' + card + '}\n';
619 var code = imports + 'Component {\n' + card + '}\n';
620+
552 return Qt.createQmlObject(code, parent, "createCardComponent");
621 return Qt.createQmlObject(code, parent, "createCardComponent");
553}
622}
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+93−91
@@ -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,107 @@ 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 }
98+ ,
99+ Label {
100+ id: subtitleLabel;
101+ objectName: "subtitleLabel";
102+ anchors { left: parent.left; right: parent.right }
103+ elide: Text.ElideRight;
104+ fontSize: "small";
105+ 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");
107+ visible: titleLabel.visible && titleLabel.text;
108+ text: cardData && cardData["subtitle"] || "";
109+ font.weight: Font.Light;
110+ horizontalAlignment: root.headerAlignment;
111+ }
112+
112−]
113+ ]
113−}
114+ }
115+
114−]
116+ ]
115−}
117+ }
116UbuntuShape {
118UbuntuShape {
117 id: touchdown;
119 id: touchdown;
118 objectName: "touchdown";
120 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+136
@@ -0,0 +1,136 @@
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+UbuntuShape {
128+ id: touchdown;
129+ objectName: "touchdown";
130+ anchors { fill: backgroundLoader }
131+ visible: root.pressed;
132+ radius: "medium";
133+ borderSource: "radius_pressed.sci"
134+ }
135+implicitHeight: row.y + row.height + units.gu(1);
136+}
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+11
@@ -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,22 @@ 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());
85+
77 const QStringList expectedLines = ts2.readAll().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
86 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");
87 const QStringList cardStringResultLines = cardStringResult.toString().trimmed().replace(QRegExp("\n\\s*\n"),"\n").split("\n");
79 for (int i = 0; i < expectedLines.size(); ++i) {
88 for (int i = 0; i < expectedLines.size(); ++i) {
80 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
89 QCOMPARE(cardStringResultLines[i].simplified(), expectedLines[i].simplified());
81 }
90 }
82
91
92+ tmpFile.setAutoRemove(true); // Remove the result if it passed
93+
83 QVariant createCardComponentResult;
94 QVariant createCardComponentResult;
84 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
95 QMetaObject::invokeMethod(view->rootObject(), "createCardComponent", Q_RETURN_ARG(QVariant, createCardComponentResult), Q_ARG(QVariant, templateJSON), Q_ARG(QVariant, componentsJSON));
85 QVERIFY(createCardComponentResult.toBool());
96 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}'
@@ -59,7 +60,8 @@ var fullMapping = ' \
59 "art": "art", \
60 "art": "art", \
60 "subtitle": "subtitle", \
61 "subtitle": "subtitle", \
61 "mascot": "mascot", \
62 "mascot": "mascot", \
62− "summary": "summary" \
63+ "summary": "summary", \
64+ "attributes": "attributes" \
63}'
65}'
64
66
65
67
tests/qmltests/Dash/tst_Card.qml+3−1
@@ -33,7 +33,8 @@ Rectangle {
33 "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
33 "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
34 "title": "foo",
34 "title": "foo",
35 "subtitle": "bar",
35 "subtitle": "bar",
36− "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."
36+ "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+ "attributes": [{"value":"text1","icon":"image://theme/ok"},{"value":"text2","icon":"image://theme/cancel"}]
37 }'
38 }'
38
39
39 property var cardsModel: [
40 property var cardsModel: [
@@ -110,6 +111,7 @@ Rectangle {
110 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
111 anchors { top: parent.top; left: parent.left; margins: units.gu(1) }
111
112
112 sourceComponent: cardTool.cardComponent
113 sourceComponent: cardTool.cardComponent
114+ clip: true
113 onLoaded: {
115 onLoaded: {
114 item.template = Qt.binding(function() { return cardTool.template; });
116 item.template = Qt.binding(function() { return cardTool.template; });
115 item.components = Qt.binding(function() { return cardTool.components; });
117 item.components = Qt.binding(function() { return cardTool.components; });