Unity 8
CardTool.qml
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.4
18 import Ubuntu.Components 1.3
19 import Dash 0.1
20 
21 /*!
22  \brief Tool for introspecting Card properties.
23 
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.
26 
27  This component creates an invisible card filled with maximum mapped data and calculates
28  or measures card properties for this configuration.
29  */
30 
31 Item {
32  id: cardTool
33 
34  /*!
35  \brief Number of cards.
36  */
37  property int count
38 
39  /*!
40  \brief Width of the category view.
41  */
42  property real viewWidth
43 
44  /*!
45  \brief Template supplied for the category.
46  */
47  property var template
48 
49  /*!
50  \brief Component mapping supplied for the category.
51  */
52  property var components
53 
54  /*!
55  \brief The Scope this cardTool is representing
56  */
57  property string scopeId
58 
59  /*!
60  \brief The Scope category this cardTool is representing
61  */
62  property string categoryId
63 
64  /*!
65  \brief Scaling factor of selected Carousel item.
66  */
67  readonly property real carouselSelectedItemScaleFactor: 1.38 // XXX assuming 1.38 carousel scaling factor for cards
68 
69  /*!
70  \brief The category layout for this card tool.
71  */
72  readonly property string categoryLayout: {
73  var layout = template["category-layout"];
74 
75  // carousel fallback mode to grid
76  if (layout === "carousel" && count <= Math.ceil(carouselTool.realPathItemCount)) layout = "grid";
77  return layout;
78  }
79 
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"))
83 
84  readonly property string artShapeStyle: {
85  if (isAppLikeScope) {
86  return isAppLikeScopeAppCategory ? "icon" : "flat";
87  } else {
88  return categoryLayout === "carousel" ? "shadow" : "flat"
89  }
90  }
91 
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 !== ""
99 
100  property var cardComponent: askForCardComponent
101  ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, false, cardTool.artShapeStyle, cardTool.categoryLayout)
102  : undefined
103 
104  // FIXME: Saviq
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.
107  width: 0
108  height: 0
109  clip: true
110 
111  // We have 3 view "widths"
112  // narrow <= 45gu
113  // normal
114  // wide >= 70gu
115  readonly property bool isWideView: viewWidth >= units.gu(70)
116  readonly property bool isNarrowView: viewWidth <= units.gu(45)
117 
118  /*!
119  type:real \brief Width to be enforced on the card in this configuration.
120 
121  If -1, should use implicit width of the actual card.
122  */
123  readonly property real cardWidth: {
124  if (isAppLikeScopeAppCategory) {
125  if (!isNarrowView) {
126  if (isWideView) {
127  return units.gu(11);
128  } else {
129  return units.gu(10);
130  }
131  } else {
132  return units.gu(12);
133  }
134  }
135 
136  switch (categoryLayout) {
137  case "grid":
138  case "vertical-journal":
139  var size = template["card-size"];
140  if (template["card-layout"] === "horizontal") size = "large";
141  switch (size) {
142  case "small": {
143  if (isNarrowView) return units.gu(12);
144  else return units.gu(14);
145  }
146  case "large": {
147  if (isWideView) return units.gu(42);
148  else return viewWidth - units.gu(2);
149  }
150  }
151  if (isNarrowView) return units.gu(18);
152  else if (isWideView) return units.gu(20);
153  else return units.gu(23);
154  case "carousel":
155  case "horizontal-list":
156  return carouselTool.minimumTileWidth;
157  case undefined:
158  case "organic-grid":
159  case "journal":
160  default:
161  return -1;
162  }
163  }
164 
165  /*!
166  type:real \brief Height to be enforced on the card in this configuration.
167 
168  If -1, should use implicit height of the actual card.
169  */
170  readonly property real cardHeight: {
171  switch (categoryLayout) {
172  case "journal":
173  if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
174  return units.gu(18.5);
175  case "grid":
176  case "horizontal-list":
177  return cardLoader.item ? cardLoader.item.implicitHeight : 0
178  case "carousel":
179  return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
180  case undefined:
181  case "organic-grid":
182  case "vertical-journal":
183  default:
184  return -1;
185  }
186  }
187 
188  /*!
189  type:real \brief Height of the card's header.
190  */
191  readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
192 
193  readonly property size artShapeSize: {
194  if (isAppLikeScopeAppCategory) {
195  return Qt.size(units.gu(8), units.gu(7.5));
196  } else {
197  return cardLoader.item ? cardLoader.item.artShapeSize : Qt.size(0, 0)
198  }
199  }
200 
201  QtObject {
202  id: carouselTool
203 
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)));
209  }
210 
211  readonly property real pathItemCount: 4.8457 /// (848 / 175) reference values
212 
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);
217  }
218  }
219 
220  Item {
221  id: attributesModel
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);
227 
228  if (hasAttributesFlag) {
229  if (attributes["max-count"]) {
230  numOfAttributes = attributes["max-count"];
231  }
232  }
233  return hasAttributesFlag
234  }
235 
236  onNumOfAttributesChanged: {
237  model = []
238  for (var i = 0; i < numOfAttributes; i++) {
239  model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
240  }
241  }
242  }
243 
244  Item {
245  id: socialActionsModel
246  property var model: []
247  readonly property bool hasActions: components["social-actions"] != undefined
248 
249  onHasActionsChanged: {
250  model = []
251  if (hasActions) {
252  model.push( {"id":"text", "icon":"image://theme/ok" } );
253  }
254  }
255  }
256 
257  Loader {
258  id: cardLoader
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"),
264  "title": "—\n—",
265  "subtitle": "—",
266  "summary": "—\n—\n—\n—\n—",
267  "attributes": attributesModel.model,
268  "socialActions": socialActionsModel.model
269  }
270  sourceComponent: askForCardComponent
271  ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, true, cardTool.artShapeStyle, cardTool.categoryLayout)
272  : undefined
273  onLoaded: {
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; });
277  }
278  Connections {
279  target: cardTool
280  onTemplateChanged: cardLoader.updateCardData();
281  onComponentsChanged: cardLoader.updateCardData();
282  }
283  function updateCardData() {
284  var data = {};
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];
293  }
294  }
295  item.cardData = data;
296  }
297  }
298 }