2 * Copyright (C) 2014 Canonical, Ltd.
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.
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.
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/>.
18 import Ubuntu.Components 1.3
22 \brief Tool for introspecting Card properties.
24 Some properties of Cards we need to determine category-wide (like card sizes in grid),
25 so we should not do them per-Card but in the category renderer.
27 This component creates an invisible card filled with maximum mapped data and calculates
28 or measures card properties for this configuration.
35 \brief Number of cards.
40 \brief Width of the category view.
42 property real viewWidth
45 \brief Template supplied for the category.
50 \brief Component mapping supplied for the category.
52 property var components
55 \brief The Scope this cardTool is representing
57 property string scopeId
60 \brief The Scope category this cardTool is representing
62 property string categoryId
65 \brief Scaling factor of selected Carousel item.
67 readonly property real carouselSelectedItemScaleFactor: 1.38 // XXX assuming 1.38 carousel scaling factor for cards
70 \brief The category layout for this card tool.
72 readonly property string categoryLayout: {
73 var layout = template["category-layout"];
75 // carousel fallback mode to grid
76 if (layout === "carousel" && count <= Math.ceil(carouselTool.realPathItemCount)) layout = "grid";
80 readonly property bool isAppLikeScope: scopeId === "clickscope" || scopeId === "libertine-scope.ubuntu_libertine-scope"
81 readonly property bool isAppLikeScopeAppCategory: ((scopeId === "clickscope" && (categoryId === "predefined" || categoryId === "local"))
82 || (scopeId === "libertine-scope.ubuntu_libertine-scope" && categoryId !== "hint"))
84 readonly property string artShapeStyle: {
86 return isAppLikeScopeAppCategory ? "icon" : "flat";
88 return categoryLayout === "carousel" ? "shadow" : "flat"
92 // FIXME ? This seems like it should not be needed, but on Qt 5.4 + phone
93 // we are doing unneeded calls to getCardComponent with artShapeStyle and categoryLayout being empty
94 // Check when we move to newer Qts on the phone if we still need this
95 readonly property bool askForCardComponent: cardTool.template !== undefined &&
96 cardTool.components !== undefined &&
97 cardTool.artShapeStyle !== "" &&
98 cardTool.categoryLayout !== ""
100 property var cardComponent: askForCardComponent
101 ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, false, cardTool.artShapeStyle, cardTool.categoryLayout)
105 // Only way for the card below to actually be laid out completely.
106 // If invisible or in "data" array, some components are not taken into account.
111 // We have 3 view "widths"
115 readonly property bool isWideView: viewWidth >= units.gu(70)
116 readonly property bool isNarrowView: viewWidth <= units.gu(45)
119 type:real \brief Width to be enforced on the card in this configuration.
121 If -1, should use implicit width of the actual card.
123 readonly property real cardWidth: {
124 if (isAppLikeScopeAppCategory) {
136 switch (categoryLayout) {
138 case "vertical-journal":
139 var size = template["card-size"];
140 if (template["card-layout"] === "horizontal") size = "large";
143 if (isNarrowView) return units.gu(12);
144 else return units.gu(14);
147 if (isWideView) return units.gu(42);
148 else return viewWidth - units.gu(2);
151 if (isNarrowView) return units.gu(18);
152 else if (isWideView) return units.gu(20);
153 else return units.gu(23);
155 case "horizontal-list":
156 return carouselTool.minimumTileWidth;
166 type:real \brief Height to be enforced on the card in this configuration.
168 If -1, should use implicit height of the actual card.
170 readonly property real cardHeight: {
171 switch (categoryLayout) {
173 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
174 return units.gu(18.5);
176 case "horizontal-list":
177 return cardLoader.item ? cardLoader.item.implicitHeight : 0
179 return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
182 case "vertical-journal":
189 type:real \brief Height of the card's header.
191 readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
193 readonly property size artShapeSize: {
194 if (isAppLikeScopeAppCategory) {
195 return Qt.size(units.gu(8), units.gu(7.5));
197 return cardLoader.item ? cardLoader.item.artShapeSize : Qt.size(0, 0)
204 readonly property real minimumTileWidth: {
205 if (cardTool.viewWidth === undefined) return undefined;
206 if (cardTool.viewWidth <= units.gu(40)) return units.gu(18);
207 if (cardTool.viewWidth >= units.gu(128)) return units.gu(26);
208 return units.gu(18 + Math.round((cardTool.viewWidth - units.gu(40)) / units.gu(11)));
211 readonly property real pathItemCount: 4.8457 /// (848 / 175) reference values
213 readonly property real realPathItemCount: {
214 var scaledMinimumTileWidth = minimumTileWidth / cardTool.carouselSelectedItemScaleFactor;
215 var tileWidth = Math.max(cardTool.viewWidth / pathItemCount, scaledMinimumTileWidth);
216 return Math.min(cardTool.viewWidth / tileWidth, pathItemCount);
222 property int numOfAttributes: 0
223 property var model: []
224 readonly property bool hasAttributes: {
225 var attributes = components["attributes"];
226 var hasAttributesFlag = (attributes != undefined) && (attributes["field"] != undefined);
228 if (hasAttributesFlag) {
229 if (attributes["max-count"]) {
230 numOfAttributes = attributes["max-count"];
233 return hasAttributesFlag
236 onNumOfAttributesChanged: {
238 for (var i = 0; i < numOfAttributes; i++) {
239 model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
245 id: socialActionsModel
246 property var model: []
247 readonly property bool hasActions: components["social-actions"] != undefined
249 onHasActionsChanged: {
252 model.push( {"id":"text", "icon":"image://theme/ok" } );
259 readonly property var cfields: ["art", "mascot", "title", "subtitle", "summary", "attributes", "social-actions"]
260 readonly property var dfields: ["art", "mascot", "title", "subtitle", "summary", "attributes", "socialActions"]
261 readonly property var maxData: {
262 "art": Qt.resolvedUrl("graphics/pixel.png"),
263 "mascot": Qt.resolvedUrl("graphics/pixel.png"),
266 "summary": "—\n—\n—\n—\n—",
267 "attributes": attributesModel.model,
268 "socialActions": socialActionsModel.model
270 sourceComponent: askForCardComponent
271 ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, true, cardTool.artShapeStyle, cardTool.categoryLayout)
274 item.objectName = "cardToolCard";
275 item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
276 item.height = Qt.binding(function() { return cardTool.cardHeight !== -1 ? cardTool.cardHeight : item.implicitHeight; });
280 onTemplateChanged: cardLoader.updateCardData();
281 onComponentsChanged: cardLoader.updateCardData();
283 function updateCardData() {
285 for (var k in cfields) {
286 var ckey = cfields[k];
287 var component = cardTool.components[ckey];
288 if ((typeof component === "string" && component.length > 0) ||
289 (typeof component === "object" && component !== null
290 && typeof component["field"] === "string" && component["field"].length > 0)) {
291 var dkey = dfields[k];
292 data[dkey] = maxData[dkey];
295 item.cardData = data;