on xenial help sign in
Add initial CardTool.
1bd3afdMichał Sawicz
qml/Dash/Card.qml+3−12
@@ -23,17 +23,8 @@ Item {
23 property var components
23 property var components
24 property var cardData
24 property var cardData
25
25
26− width: {
26+ implicitWidth: childrenRect.width
27− if (template !== undefined) {
28− if (template["card-layout"] === "horizontal") return units.gu(38);
29− switch (template['card-size']) {
30− case "small": return units.gu(12);
31− case "large": return units.gu(38);
32− }
33− }
34− return units.gu(18.5);
35− }
36− height: childrenRect.height
27+ implicitHeight: childrenRect.height
37
28
38 UbuntuShape {
29 UbuntuShape {
39 id: artShape
30 id: artShape
@@ -60,7 +51,7 @@ Item {
60 // FIXME uncomment when having investigated / fixed the crash
51 // FIXME uncomment when having investigated / fixed the crash
61 //sourceSize.width: width > height ? width : 0
52 //sourceSize.width: width > height ? width : 0
62 //sourceSize.height: height > width ? height : 0
53 //sourceSize.height: height > width ? height : 0
63− fillMode: components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
54+ fillMode: components && components["art"]["fill-mode"] === "fit" ? Image.PreserveAspectFit: Image.PreserveAspectCrop
64
55
65 property real aspect: implicitWidth / implicitHeight
56 property real aspect: implicitWidth / implicitHeight
66 }
57 }
qml/Dash/CardTool.qmlnew file+89
@@ -0,0 +1,89 @@
1+/*
2+ * Copyright (C) 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.0
18+
19+QtObject {
20+ id: cardTool
21+ property var template
22+ property var components
23+
24+ property var cardWidth: {
25+ switch (template !== undefined && template["category-layout"]) {
26+ case "grid":
27+ case "vertical-journal":
28+ if (template["card-layout"] === "horizontal") return units.gu(38);
29+ switch (template["card-size"]) {
30+ case "small": return units.gu(12);
31+ case "large": return units.gu(38);
32+ }
33+ return units.gu(18.5);
34+ case undefined:
35+ case "organic-grid":
36+ case "journal":
37+ default:
38+ return undefined;
39+ }
40+ }
41+
42+ property var cardHeight: {
43+ switch (template !== undefined && template["category-layout"]) {
44+ case "journal":
45+ if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
46+ return units.gu(18.5);
47+ case "grid":
48+ return priv.card.implicitHeight
49+ case undefined:
50+ case "organic-grid":
51+ case "vertical-journal":
52+ default:
53+ return undefined;
54+ }
55+ }
56+
57+ property QtObject priv: QtObject {
58+ property var fields: ["art", "mascot", "title", "subtitle", "summary"]
59+ property var maxData: {
60+ "art": Qt.resolvedUrl("graphics/checkers.png"),
61+ "mascot": Qt.resolvedUrl("graphics/checkers.png"),
62+ "title": "—\n—",
63+ "subtitle": "—",
64+ "summary": "—\n—\n—\n—\n—"
65+ }
66+
67+ property Item card: Card {
68+ template: cardTool.template
69+ components: cardTool.components
70+
71+ width: cardTool.cardWidth || implicitWidth
72+ height: cardTool.cardHeight || implicitHeight
73+
74+ onComponentsChanged: {
75+ var data = {};
76+ for (var k in priv.fields) {
77+ var component = components[priv.fields[k]];
78+ var key = priv.fields[k];
79+ if ((typeof component === "string" && component.length > 0) ||
80+ (typeof component === "object" && component !== null
81+ && typeof component["field"] === "string" && component["field"].length > 0)) {
82+ data[key] = priv.maxData[key];
83+ }
84+ }
85+ cardData = data;
86+ }
87+ }
88+ }
89+}
qml/Dash/graphics/checkers.pngnew file

Binary file not shown.

tests/qmltests/CMakeLists.txt+1
@@ -44,6 +44,7 @@ add_qml_test(Dash DashPreview)
44add_qml_test(Dash GenericPreview)
44add_qml_test(Dash GenericPreview)
45add_qml_test(Dash Card)
45add_qml_test(Dash Card)
46add_qml_test(Dash CardHeader)
46add_qml_test(Dash CardHeader)
47+add_qml_test(Dash CardTool)
47add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
48add_qml_test(Dash GenericScopeView IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS})
48add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
49add_qml_test(Dash FilterGrids IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins ${CMAKE_CURRENT_SOURCE_DIR}/plugins
49 ${CMAKE_BINARY_DIR}/tests/mocks)
50 ${CMAKE_BINARY_DIR}/tests/mocks)
tests/qmltests/Dash/tst_Card.qml+2−2
@@ -245,8 +245,8 @@ Rectangle {
245 function test_card_size_data() {
245 function test_card_size_data() {
246 return [
246 return [
247 { tag: "Medium", width: units.gu(18.5), index: 0 },
247 { tag: "Medium", width: units.gu(18.5), index: 0 },
248− { tag: "Small", width: units.gu(12), size: "small", index: 0 },
248+ { tag: "Small", width: units.gu(12), index: 1 },
249− { tag: "Large", width: units.gu(38), size: "large", index: 0 },
249+ { tag: "Large", width: units.gu(38), index: 2 },
250 { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
250 { tag: "Wide", width: units.gu(18.5), aspect: 0.5, index: 0 },
251 { tag: "Horizontal", width: units.gu(38), index: 5 },
251 { tag: "Horizontal", width: units.gu(38), index: 5 },
252 // Make sure card ends with header when there's no summary
252 // Make sure card ends with header when there's no summary
tests/qmltests/Dash/tst_CardTool.qmlnew file+277
@@ -0,0 +1,277 @@
1+/*
2+ * Copyright (C) 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.0
18+import QtTest 1.0
19+import Ubuntu.Components 0.1
20+import Unity.Test 0.1 as UT
21+import "../../../qml/Dash"
22+import "CardHelpers.js" as Helpers
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(80)
27+ height: units.gu(72)
28+ color: "#88FFFFFF"
29+
30+ property string defaultLayout: '
31+ {
32+ "schema-version": 1,
33+ "template": {
34+ "category-layout": "grid",
35+ "card-layout": "vertical",
36+ "card-size": "medium",
37+ "overlay-mode": null,
38+ "collapsed-rows": 2
39+ },
40+ "components": {
41+ "title": null,
42+ "art": {
43+ "aspect-ratio": 1.0,
44+ "fill-mode": "crop"
45+ },
46+ "subtitle": null,
47+ "mascot": null,
48+ "emblem": null,
49+ "old-price": null,
50+ "price": null,
51+ "alt-price": null,
52+ "rating": {
53+ "type": "stars",
54+ "range": [0, 5],
55+ "full": "image://theme/rating-star-full",
56+ "half": "image://theme/rating-star-half",
57+ "empty": "image://theme/rating-star-empty"
58+ },
59+ "alt-rating": null,
60+ "summary": null
61+ },
62+ "resources": {}
63+ }'
64+
65+ property string fullMapping: '
66+ {
67+ "title": "title",
68+ "art": "art",
69+ "subtitle": "subtitle",
70+ "mascot": "mascot",
71+ "summary": "summary"
72+ }'
73+
74+ property var cardsModel: [
75+ {
76+ "name": "Art, header, summary - vertical",
77+ "layout": { "components": JSON.parse(fullMapping) }
78+ },
79+ {
80+ "name": "Art, header, summary - vertical, small",
81+ "layout": { "template": { "card-size": "small" }, "components": JSON.parse(fullMapping) }
82+ },
83+ {
84+ "name": "Art, header, summary - vertical, large",
85+ "layout": { "template": { "card-size": "large" }, "components": JSON.parse(fullMapping) }
86+ },
87+ {
88+ "name": "Header title only - horizontal",
89+ "layout": { "template": { "card-layout": "horizontal" },
90+ "components": { "title": "title" } }
91+ },
92+ ]
93+
94+ CardTool {
95+ id: cardTool
96+
97+ template: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['template'];
98+ components: Helpers.update(JSON.parse(root.defaultLayout), Helpers.tryParse(layoutArea.text, layoutError))['components'];
99+ }
100+
101+ Column {
102+ width: units.gu(38)
103+ anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
104+
105+ Repeater {
106+ model: [
107+ { label: "Card width", value: cardTool.cardWidth && cardTool.cardWidth / units.gu(1) },
108+ { label: "Card height", value: cardTool.cardHeight && cardTool.cardHeight / units.gu(1) },
109+ ]
110+
111+ delegate: Row {
112+ anchors { left: parent.left; right: parent.right; margins: units.gu(3) }
113+
114+ Label {
115+ height: units.gu(5)
116+ width: units.gu(25)
117+ text: modelData.label
118+ verticalAlignment: Text.AlignVCenter
119+ }
120+
121+ Label {
122+ height: units.gu(5)
123+ text: modelData.value || "undefined"
124+ verticalAlignment: Text.AlignVCenter
125+ }
126+ }
127+ }
128+
129+ Card {
130+ template: cardTool.template;
131+ components: cardTool.components;
132+ cardData: Helpers.mapData(dataArea.text, components, dataError)
133+
134+ width: cardTool.cardWidth || implicitWidth
135+ height: cardTool.cardHeight || implicitHeight
136+
137+// clip: true
138+
139+ Rectangle {
140+ anchors.fill: parent
141+ color: "transparent"
142+ border.width: 1
143+ border.color: "green"
144+ }
145+ }
146+ }
147+
148+ Rectangle {
149+ anchors { top: parent.top; bottom: parent.bottom; right: parent.right}
150+ width: units.gu(40)
151+ color: "lightgrey"
152+
153+ Column {
154+ anchors { fill: parent; margins: units.gu(1) }
155+ spacing: units.gu(1)
156+
157+ OptionSelector {
158+ id: selector
159+ model: cardsModel
160+ delegate: OptionSelectorDelegate { text: modelData.name }
161+ onSelectedIndexChanged: updateAreas()
162+ Component.onCompleted: updateAreas()
163+
164+ function updateAreas() {
165+ var element = cardsModel[selectedIndex];
166+ if (element) {
167+ layoutArea.text = JSON.stringify(element.layout, undefined, 2) || "{}";
168+ } else {
169+ layoutArea.text = "";
170+ }
171+ }
172+ }
173+
174+ OptionSelector {
175+ id: layoutSelector
176+ model: [
177+ { label: "Grid", layout: "grid" },
178+ { label: "Organic Grid", layout: "organic-grid" },
179+ { label: "Horizontal Journal", layout: "journal" },
180+ { label: "Vertical Journal", layout: "vertical-journal" },
181+ ]
182+ delegate: OptionSelectorDelegate { text: modelData.label }
183+ onSelectedIndexChanged: {
184+ var element = model[selectedIndex];
185+ if (element && cardTool.template["category-layout"] !== element.layout) {
186+ var current = JSON.parse(layoutArea.text);
187+ var update = { "template": { "category-layout": element.layout }}
188+ layoutArea.text = JSON.stringify(Helpers.update(current, update), undefined, 2) || "{}";
189+ }
190+ }
191+ }
192+
193+ TextArea {
194+ id: layoutArea
195+ anchors { left: parent.left; right: parent.right }
196+ height: units.gu(25)
197+ }
198+
199+ Label {
200+ id: layoutError
201+ anchors { left: parent.left; right: parent.right }
202+ height: units.gu(4)
203+ color: "orange"
204+ }
205+
206+ TextArea {
207+ id: dataArea
208+ anchors { left: parent.left; right: parent.right }
209+ height: units.gu(25)
210+ text: JSON.stringify(cardTool.priv.card.cardData, undefined, 2)
211+ }
212+
213+ Label {
214+ id: dataError
215+ anchors { left: parent.left; right: parent.right }
216+ height: units.gu(4)
217+ color: "orange"
218+ }
219+ }
220+
221+ Connections {
222+ target: cardTool
223+ onTemplateChanged: {
224+ switch(cardTool.template["category-layout"]) {
225+ case "organic-grid": layoutSelector.selectedIndex = 1; break;
226+ case "journal": layoutSelector.selectedIndex = 2; break;
227+ case "vertical-journal": layoutSelector.selectedIndex = 3; break;
228+ case "grid":
229+ default: layoutSelector.selectedIndex = 0; break;
230+ }
231+ }
232+ }
233+ }
234+
235+ UT.UnityTestCase {
236+ id: testCase
237+ name: "Card"
238+
239+ when: windowShown
240+
241+ function cleanup() {
242+ selector.selectedIndex = -1;
243+ layoutSelector.selectedIndex = -1;
244+ }
245+
246+ function test_card_size_data() {
247+ return [
248+ { tag: "Medium", width: units.gu(18.5), height: undefined, index: 0 },
249+ { tag: "Small", width: units.gu(12), height: undefined, index: 1 },
250+ { tag: "Large", width: units.gu(38), height: undefined, index: 2 },
251+ { tag: "Horizontal", width: units.gu(38), height: undefined, index: 3 },
252+ { tag: "Journal", width: undefined, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
253+ { tag: "OversizedJournal", width: undefined, height: units.gu(18.5), size: 40, index: 0, layout_index: 2 },
254+ ]
255+ }
256+
257+ function test_card_size(data) {
258+ selector.selectedIndex = data.index;
259+ if (data.hasOwnProperty("layout_index")) {
260+ layoutSelector.selectedIndex = data.layout_index;
261+ }
262+
263+ if (data.hasOwnProperty("size")) {
264+ cardTool.template['card-size'] = data.size;
265+ cardTool.templateChanged();
266+ }
267+
268+ if (data.hasOwnProperty("width")) {
269+ tryCompare(cardTool, "cardWidth", data.width);
270+ }
271+
272+ if (data.hasOwnProperty("height")) {
273+ tryCompare(cardTool, "cardHeight", data.height);
274+ }
275+ }
276+ }
277+}