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