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 Dash 0.1
19 
20 /*!
21  \brief Tool for introspecting Card properties.
22 
23  Some properties of Cards we need to determine category-wide (like card sizes in grid),
24  so we should not do them per-Card but in the category renderer.
25 
26  This component creates an invisible card filled with maximum mapped data and calculates
27  or measures card properties for this configuration.
28  */
29 
30 Item {
31  id: cardTool
32 
33  /*!
34  \brief Number of cards.
35  */
36  property int count
37 
38  /*!
39  \brief Width of the category view.
40  */
41  property real viewWidth
42 
43  /*!
44  \brief Scaling factor of selected Carousel item.
45  */
46  readonly property real carouselSelectedItemScaleFactor: 1.38 // XXX assuming 1.38 carousel scaling factor for cards
47 
48  /*!
49  \brief Template supplied for the category.
50  */
51  property var template
52 
53  /*!
54  \brief Component mapping supplied for the category.
55  */
56  property var components
57 
58  /*!
59  \brief The category layout for this card tool.
60  */
61  property string categoryLayout: {
62  var layout = template["category-layout"];
63 
64  // carousel fallback mode to grid
65  if (layout === "carousel" && count <= Math.ceil(carouselTool.realPathItemCount)) layout = "grid";
66  return layout;
67  }
68 
69 
70  // Not readonly because gets overwritten from GenericScopeView in some cases
71  property string artShapeStyle: categoryLayout === "carousel" ? "shadow" : "inset"
72 
73  property var cardComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, false, artShapeStyle);
74 
75  // FIXME: Saviq
76  // Only way for the card below to actually be laid out completely.
77  // If invisible or in "data" array, some components are not taken into account.
78  width: 0
79  height: 0
80  clip: true
81 
82  /*!
83  type:real \brief Width to be enforced on the card in this configuration.
84 
85  If -1, should use implicit width of the actual card.
86  */
87  property real cardWidth: {
88  switch (categoryLayout) {
89  case "grid":
90  case "vertical-journal":
91  var size = template["card-size"];
92  if (template["card-layout"] === "horizontal") size = "large";
93  switch (size) {
94  case "small": {
95  if (viewWidth <= units.gu(45)) return units.gu(12);
96  else return units.gu(14);
97  }
98  case "large": {
99  if (viewWidth >= units.gu(70)) return units.gu(42);
100  else return viewWidth - units.gu(2);
101  }
102  }
103  if (viewWidth <= units.gu(45)) return units.gu(18);
104  else if (viewWidth >= units.gu(70)) return units.gu(20);
105  else return units.gu(23);
106  case "carousel":
107  case "horizontal-list":
108  return carouselTool.minimumTileWidth;
109  case undefined:
110  case "organic-grid":
111  case "journal":
112  default:
113  return -1;
114  }
115  }
116 
117  /*!
118  type:real \brief Height to be enforced on the card in this configuration.
119 
120  If -1, should use implicit height of the actual card.
121  */
122  readonly property real cardHeight: {
123  switch (categoryLayout) {
124  case "journal":
125  if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
126  return units.gu(18.5);
127  case "grid":
128  case "horizontal-list":
129  return cardLoader.item ? cardLoader.item.implicitHeight : 0
130  case "carousel":
131  return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
132  case undefined:
133  case "organic-grid":
134  case "vertical-journal":
135  default:
136  return -1;
137  }
138  }
139 
140  /*!
141  type:real \brief Height of the card's header.
142  */
143  readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
144  property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
145 
146  QtObject {
147  id: carouselTool
148 
149  property real minimumTileWidth: {
150  if (cardTool.viewWidth === undefined) return undefined;
151  if (cardTool.viewWidth <= units.gu(40)) return units.gu(18);
152  if (cardTool.viewWidth >= units.gu(128)) return units.gu(26);
153  return units.gu(18 + Math.round((cardTool.viewWidth - units.gu(40)) / units.gu(11)));
154  }
155 
156  readonly property real pathItemCount: 4.8457 /// (848 / 175) reference values
157 
158  property real realPathItemCount: {
159  var scaledMinimumTileWidth = minimumTileWidth / cardTool.carouselSelectedItemScaleFactor;
160  var tileWidth = Math.max(cardTool.viewWidth / pathItemCount, scaledMinimumTileWidth);
161  return Math.min(cardTool.viewWidth / tileWidth, pathItemCount);
162  }
163  }
164 
165  Item {
166  id: attributesModel
167  property int numOfAttributes: 0
168  property var model: []
169  readonly property bool hasAttributes: {
170  var attributes = components["attributes"];
171  var hasAttributesFlag = (attributes != undefined) && (attributes["field"] != undefined);
172 
173  if (hasAttributesFlag) {
174  if (attributes["max-count"]) {
175  numOfAttributes = attributes["max-count"];
176  }
177  }
178  return hasAttributesFlag
179  }
180 
181  onNumOfAttributesChanged: {
182  model = []
183  for (var i = 0; i < numOfAttributes; i++) {
184  model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
185  }
186  }
187  }
188 
189  Item {
190  id: socialActionsModel
191  property var model: []
192  readonly property bool hasActions: components["social-actions"] != undefined
193 
194  onHasActionsChanged: {
195  model = []
196  if (hasActions) {
197  model.push( {"id":"text", "icon":"image://theme/ok" } );
198  }
199  }
200  }
201 
202  Loader {
203  id: cardLoader
204  readonly property var cfields: ["art", "mascot", "title", "subtitle", "summary", "attributes", "social-actions"]
205  readonly property var dfields: ["art", "mascot", "title", "subtitle", "summary", "attributes", "socialActions"]
206  readonly property var maxData: {
207  "art": Qt.resolvedUrl("graphics/pixel.png"),
208  "mascot": Qt.resolvedUrl("graphics/pixel.png"),
209  "title": "—\n—",
210  "subtitle": "—",
211  "summary": "—\n—\n—\n—\n—",
212  "attributes": attributesModel.model,
213  "socialActions": socialActionsModel.model
214  }
215  sourceComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, true, artShapeStyle);
216  onLoaded: {
217  item.objectName = "cardToolCard";
218  item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
219  item.height = Qt.binding(function() { return cardTool.cardHeight !== -1 ? cardTool.cardHeight : item.implicitHeight; });
220  }
221  Connections {
222  target: cardTool
223  onTemplateChanged: cardLoader.updateCardData();
224  onComponentsChanged: cardLoader.updateCardData();
225  }
226  function updateCardData() {
227  var data = {};
228  for (var k in cfields) {
229  var ckey = cfields[k];
230  var component = cardTool.components[ckey];
231  if ((typeof component === "string" && component.length > 0) ||
232  (typeof component === "object" && component !== null
233  && typeof component["field"] === "string" && component["field"].length > 0)) {
234  var dkey = dfields[k];
235  data[dkey] = maxData[dkey];
236  }
237  }
238  item.cardData = data;
239  }
240  }
241 }