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  property var cardComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components);
70 
71  // FIXME: Saviq
72  // Only way for the card below to actually be laid out completely.
73  // If invisible or in "data" array, some components are not taken into account.
74  width: 0
75  height: 0
76  clip: true
77 
78  /*!
79  type:real \brief Width to be enforced on the card in this configuration.
80 
81  If undefined, should use implicit width of the actual card.
82  */
83  property var cardWidth: {
84  switch (categoryLayout) {
85  case "grid":
86  case "vertical-journal":
87  var size = template["card-size"];
88  if (template["card-layout"] === "horizontal") size = "large";
89  switch (size) {
90  case "small": {
91  if (viewWidth <= units.gu(45)) return units.gu(12);
92  else return units.gu(14);
93  }
94  case "large": {
95  if (viewWidth >= units.gu(70)) return units.gu(42);
96  else return viewWidth - units.gu(2);
97  }
98  }
99  if (viewWidth <= units.gu(45)) return units.gu(18);
100  else if (viewWidth >= units.gu(70)) return units.gu(20);
101  else return units.gu(23);
102  case "carousel":
103  case "horizontal-list":
104  return carouselTool.minimumTileWidth;
105  case undefined:
106  case "organic-grid":
107  case "journal":
108  default:
109  return undefined;
110  }
111  }
112 
113  /*!
114  type:real \brief Height to be enforced on the card in this configuration.
115 
116  If undefined, should use implicit height of the actual card.
117  */
118  readonly property var cardHeight: {
119  switch (categoryLayout) {
120  case "journal":
121  if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
122  return units.gu(18.5);
123  case "grid":
124  case "horizontal-list":
125  return cardLoader.item ? cardLoader.item.implicitHeight : 0
126  case "carousel":
127  return cardWidth / (components ? components["art"]["aspect-ratio"] : 1)
128  case undefined:
129  case "organic-grid":
130  case "vertical-journal":
131  default:
132  return undefined;
133  }
134  }
135 
136  /*!
137  type:real \brief Height of the card's header.
138  */
139  readonly property int headerHeight: cardLoader.item ? cardLoader.item.headerHeight : 0
140  property size artShapeSize: cardLoader.item ? cardLoader.item.artShapeSize : 0
141 
142  /*!
143  \brief Desired alignment of title
144  */
145  readonly property int titleAlignment: {
146  if (template["card-layout"] === "horizontal"
147  || typeof components["title"] !== "object"
148  || components["title"]["align"] === "left") return Text.AlignLeft;
149 
150  var keys = ["mascot", "emblem", "subtitle", "attributes", "summary"];
151 
152  for (var key in keys) {
153  key = keys[key];
154  try {
155  if (typeof components[key] === "string"
156  || typeof components[key]["field"] === "string") return Text.AlignLeft;
157  } catch (e) {
158  continue;
159  }
160  }
161 
162  return Text.AlignHCenter;
163  }
164 
165  QtObject {
166  id: carouselTool
167 
168  property real minimumTileWidth: {
169  if (cardTool.viewWidth === undefined) return undefined;
170  if (cardTool.viewWidth <= units.gu(40)) return units.gu(18);
171  if (cardTool.viewWidth >= units.gu(128)) return units.gu(26);
172  return units.gu(18 + Math.round((cardTool.viewWidth - units.gu(40)) / units.gu(11)));
173  }
174 
175  readonly property real pathItemCount: 4.8457 /// (848 / 175) reference values
176 
177  property real realPathItemCount: {
178  var scaledMinimumTileWidth = minimumTileWidth / cardTool.carouselSelectedItemScaleFactor;
179  var tileWidth = Math.max(cardTool.viewWidth / pathItemCount, scaledMinimumTileWidth);
180  return Math.min(cardTool.viewWidth / tileWidth, pathItemCount);
181  }
182  }
183 
184  Item {
185  id: attributesModel
186  property int numOfAttributes: 0
187  property var model: []
188  property bool hasAttributes: {
189  var attributes = components["attributes"];
190  var hasAttributesFlag = (attributes != undefined) && (attributes["field"] != undefined);
191 
192  if (hasAttributesFlag) {
193  if (attributes["max-count"]) {
194  numOfAttributes = attributes["max-count"];
195  }
196  }
197  return hasAttributesFlag
198  }
199 
200  onNumOfAttributesChanged: {
201  model = []
202  for (var i = 0; i < numOfAttributes; i++) {
203  model.push( {"value":"text"+(i+1), "icon":"image://theme/ok" } );
204  }
205  }
206  }
207 
208  Loader {
209  id: cardLoader
210  readonly property var fields: ["art", "mascot", "title", "subtitle", "summary", "attributes"]
211  readonly property var maxData: {
212  "art": Qt.resolvedUrl("graphics/pixel.png"),
213  "mascot": Qt.resolvedUrl("graphics/pixel.png"),
214  "title": "—\n—",
215  "subtitle": "—",
216  "summary": "—\n—\n—\n—\n—",
217  "attributes": attributesModel.model
218  }
219  sourceComponent: cardTool.cardComponent
220  onLoaded: {
221  item.objectName = "cardToolCard";
222  item.asynchronous = false;
223  item.components = Qt.binding(function() { return cardTool.components; });
224  item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
225  item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
226  }
227  Connections {
228  target: cardTool
229  onTemplateChanged: cardLoader.updateCardData();
230  onComponentsChanged: cardLoader.updateCardData();
231  }
232  function updateCardData() {
233  var data = {};
234  for (var k in fields) {
235  var component = cardTool.components[fields[k]];
236  var key = fields[k];
237  if ((typeof component === "string" && component.length > 0) ||
238  (typeof component === "object" && component !== null
239  && typeof component["field"] === "string" && component["field"].length > 0)) {
240  data[key] = maxData[key];
241  }
242  }
243  item.cardData = data;
244  }
245  }
246 }