on xenial help sign in
Add attribute UI.
commitc667d99d4fdb5f5b3e064f70bc9863487d77cd51 authorYing-Chun Liu <paul.liu@canonical.com> dateMon 21 Jul 2014, 23:40 (12 years ago) parent70f90d1
9 files changed+280−6
plugins/Dash/CardAttributesGrid.qml+44 plugins/Dash/CardCreator.js+45−2 plugins/Dash/qmldir+1 tests/plugins/Dash/CMakeLists.txt+1 tests/plugins/Dash/cardcreator/7.res+128 tests/plugins/Dash/cardcreator/7.tst+3 tests/plugins/Dash/tst_CardAttributesGrid.qml+51 tests/qmltests/Dash/CardHelpers.js+5−3 tests/qmltests/Dash/tst_Card.qml+2−1
plugins/Dash/CardAttributesGrid.qmlnew file+44
@@ -0,0 +1,44 @@
1+import QtQuick 2.2
2+import QtQuick.Layouts 1.1
3+import Ubuntu.Components 1.1
4+import Ubuntu.Settings.Components 0.1
5+
6+GridLayout {
7+ id: grid
8+ anchors {
9+ left: parent.left;
10+ right: parent.right;
11+ }
12+ columns: 2 + repeater.count % 2
13+ property alias model: repeater.model
14+ Repeater {
15+ id: repeater
16+ delegate: Row {
17+ spacing: units.gu(0.5)
18+ readonly property int column: index % grid.columns;
19+ Layout.alignment: {
20+ if (column == 0) return Qt.AlignLeft;
21+ if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
22+ if (column == 1) return Qt.AlignHCenter;
23+ }
24+ Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
25+ Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
26+ Layout.fillWidth: true
27+ StatusIcon {
28+ id: icon
29+ height: units.gu(2)
30+ sets: ["actions", "status", "apps"]
31+ source: "icon" in modelData ? modelData["icon"] : ""
32+ }
33+ Label {
34+ id: label
35+ width: parent.width - x
36+ anchors.verticalCenter: parent.verticalCenter
37+ text: "value" in modelData ? modelData["value"] : "";
38+ elide: Text.ElideRight
39+ maximumLineCount: 1
40+ font.weight: "style" in modelData && modelData["style"] == "highlighted" ? Font.DemiBold : Font.Light
41+ }
42+ }
43+ }
44+}
plugins/Dash/CardCreator.js+45−2
@@ -193,6 +193,21 @@ var kHeaderColumnCode = 'Column { \n\
193193 ] \n\
194194 }\n';
195195
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+
196211// %1 is used as anchors of mascotShapeLoader
197212var kMascotShapeLoaderCode = 'Loader { \n\
198213 id: mascotShapeLoader; \n\
@@ -224,7 +239,7 @@ var kMascotImageCode = 'Image { \n\
224239 }\n';
225240
226241// %1 is used as anchors of titleLabel
227−// %1 is used as color of titleLabel
242+// %2 is used as color of titleLabel
228243// %3 is used as extra condition for visible of titleLabel
229244var kTitleLabelCode = 'Label { \n\
230245 id: titleLabel; \n\
@@ -258,6 +273,15 @@ var kSubtitleLabelCode = 'Label { \n\
258273 horizontalAlignment: root.headerAlignment; \n\
259274 }\n';
260275
276+// %1 is used as anchors of attributesRow
277+var kAttributesRowCode = 'CardAttributesGrid { \n\
278+ id: attributesRow; \n\
279+ objectName: "attributesRow"; \n\
280+ height: units.gu(1.5); \n\
281+ anchors { %1 } \n\
282+ model: cardData["attributes"] \n\
283+ }\n';
284+
261285// %1 is used as top anchor of summary
262286// %2 is used as topMargin anchor of summary
263287// %3 is used as color of summary
@@ -308,6 +332,7 @@ function cardString(template, components) {
308332 var headerAsOverlay = hasArt && template && template["overlay"] === true && (hasTitle || hasMascot);
309333 var hasSubtitle = hasTitle && components["subtitle"] || false;
310334 var hasHeaderRow = hasMascot && hasTitle;
335+ var hasAttributes = components["attributes"] || false;
311336
312337 if (hasBackground) {
313338 code += kBackgroundLoaderCode;
@@ -377,6 +402,8 @@ function cardString(template, components) {
377402 code += 'readonly property int headerHeight: row.height;\n'
378403 } else if (hasMascot) {
379404 code += 'readonly property int headerHeight: mascotImage.height;\n'
405+ } else if (hasAttributes) {
406+ code += 'readonly property int headerHeight: titleLabel.height + titleLabel.anchors.topMargin + subtitleLabel.height + subtitleLabel.anchors.topMargin + attributesRow.height + attributesRow.anchors.topMargin;\n'
380407 } else if (hasSubtitle) {
381408 code += 'readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;\n'
382409 } else if (hasTitle) {
@@ -425,10 +452,12 @@ function cardString(template, components) {
425452
426453 var titleAnchors;
427454 var subtitleAnchors;
455+ var attributesAnchors;
428456 if (hasMascot && hasSubtitle) {
429457 // Using row + column
430458 titleAnchors = 'left: parent.left; right: parent.right';
431459 subtitleAnchors = titleAnchors;
460+ attributesAnchors = subtitleAnchors;
432461 } else if (hasMascot) {
433462 // Using row + label
434463 titleAnchors = 'verticalCenter: parent.verticalCenter;\n'
@@ -454,6 +483,11 @@ function cardString(template, components) {
454483 right: titleLabel.right; \n\
455484 top: titleLabel.bottom; \n\
456485 topMargin: units.dp(2);\n';
486+ attributesAnchors = 'left: subtitleLabel.left; \n\
487+ leftMargin: subtitleLabel.leftMargin; \n\
488+ right: subtitleLabel.right; \n\
489+ top: subtitleLabel.bottom; \n\
490+ topMargin: units.dp(2);\n';
457491 }
458492
459493 var titleLabelVisibleExtra = (headerAsOverlay ? '&& overlayLoader.active': '');
@@ -466,8 +500,16 @@ function cardString(template, components) {
466500 if (hasMascot && hasSubtitle) {
467501 // If using row + column wrap the code in the column
468502 titleSubtitleCode = kHeaderColumnCode.arg(titleCode).arg(subtitleCode);
503+ if (hasAttributes) {
504+ var attributesCode = kAttributesRowCode.arg(attributesAnchors);
505+ titleSubtitleCode = kHeaderColumnCodeGenerator(titleCode, subtitleCode, attributesCode);
506+ }
469507 } else {
470508 titleSubtitleCode = titleCode + subtitleCode;
509+ if (hasAttributes) {
510+ var attributesCode = kAttributesRowCode.arg(attributesAnchors);
511+ titleSubtitleCode = titleSubtitleCode + attributesCode;
512+ }
471513 }
472514 }
473515
@@ -526,7 +568,8 @@ function cardString(template, components) {
526568function createCardComponent(parent, template, components) {
527569 var imports = 'import QtQuick 2.2; \n\
528570 import Ubuntu.Components 0.1; \n\
529− import Ubuntu.Thumbnailer 0.1;\n';
571+ import Ubuntu.Thumbnailer 0.1;\n\
572+ import Dash 0.1;\n';
530573 var card = cardString(template, components);
531574 var code = imports + 'Component {\n' + card + '}\n';
532575 return Qt.createQmlObject(code, parent, "createCardComponent");
plugins/Dash/qmldir+1
@@ -3,3 +3,4 @@ plugin Dash-qml
33typeinfo Dash.qmltypes
44singleton CardCreatorCache 0.1 CardCreatorCache.qml
55ScopeStyle 0.1 ScopeStyle.qml
6+CardAttributesGrid 0.1 CardAttributesGrid.qml
tests/plugins/Dash/CMakeLists.txt+1
@@ -79,3 +79,4 @@ set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
7979set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
8080add_qml_test(. ScopeStyle IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
8181add_qml_test(. ListViewWithPageHeaderQML IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
82+add_qml_test(. CardAttributesGrid IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins)
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+CardAttributesGrid {
115+ id: attributesRow;
116+ objectName: "attributesRow";
117+ height: units.gu(1.5);
118+ anchors { left: parent.left; right: parent.right }
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":[{"text":"text1","icon":"search"},{"text":"text2","icon":"cancel"}]}
3+result: 7.res
tests/plugins/Dash/tst_CardAttributesGrid.qmlnew file+51
@@ -0,0 +1,51 @@
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(30)
24+
25+ property var testData: [
26+ [{"value":"text1"},{"value":"text2"}],
27+ [{"value":"text1"},{"value":"text2"},{"value":"text3"}]
28+ ]
29+
30+ CardAttributesGrid {
31+ id: cardAttributesGrid
32+ model: testData[0]
33+ }
34+
35+ TestCase {
36+ name: "CardAttributesGridTest"
37+ when: windowShown
38+
39+ function init() {
40+ }
41+
42+ function test_columns_data() {
43+ return testData;
44+ }
45+
46+ function test_columns(data) {
47+ cardAttributesGrid.model = data;
48+ compare(cardAttributesGrid.columns, data.length);
49+ }
50+ }
51+}
tests/qmltests/Dash/CardHelpers.js+5−3
@@ -16,7 +16,7 @@
1616
1717.pragma library
1818
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"]
2020
2121var defaultLayout = ' \
2222{ \
@@ -48,7 +48,8 @@ var defaultLayout = ' \
4848 "empty": "image://theme/rating-star-empty" \
4949 }, \
5050 "alt-rating": null, \
51− "summary": null \
51+ "summary": null, \
52+ "attributes": null \
5253 }, \
5354 "resources": {} \
5455}'
@@ -59,7 +60,8 @@ var fullMapping = ' \
5960 "art": "art", \
6061 "subtitle": "subtitle", \
6162 "mascot": "mascot", \
62− "summary": "summary" \
63+ "summary": "summary", \
64+ "attributes": "attributes" \
6365}'
6466
6567
tests/qmltests/Dash/tst_Card.qml+2−1
@@ -33,7 +33,8 @@ Rectangle {
3333 "mascot": "../../../tests/qmltests/Dash/artwork/avatar.png",
3434 "title": "foo",
3535 "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"},{"value":"text2"}]
3738 }'
3839
3940 property var cardsModel: [