Unity 8
 All Classes Functions Properties
GenericScopeView.qml
1 /*
2  * Copyright (C) 2013 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.0
18 import Ubuntu.Components 0.1
19 import Utils 0.1
20 import Unity 0.2
21 import Unity.Application 0.1
22 import "../Components"
23 import "../Components/ListItems" as ListItems
24 
25 FocusScope {
26  id: scopeView
27 
28  property Scope scope: null
29  property SortFilterProxyModel categories: categoryFilter
30  property bool isCurrent: false
31  property alias moving: categoryView.moving
32  property int tabBarHeight: 0
33  property PageHeader pageHeader: null
34  property Item previewListView: null
35 
36  property bool enableHeightBehaviorOnNextCreation: false
37  property var categoryView: categoryView
38 
39  onScopeChanged: {
40  if (scope) {
41  scope.activateApplication.connect(activateApp);
42  }
43  }
44 
45  function activateApp(appId) {
46  shell.activateApplication(appId);
47  }
48 
49  function positionAtBeginning() {
50  categoryView.positionAtBeginning()
51  }
52 
53  function showHeader() {
54  categoryView.showHeader()
55  }
56 
57  Binding {
58  target: scope
59  property: "isActive"
60  value: isCurrent && !previewListView.open
61  }
62 
63  SortFilterProxyModel {
64  id: categoryFilter
65  model: scope ? scope.categories : null
66  dynamicSortFilter: true
67  filterRole: Categories.RoleCount
68  filterRegExp: /^0$/
69  invertMatch: true
70  }
71 
72  onIsCurrentChanged: {
73  pageHeader.resetSearch();
74  previewListView.open = false;
75  }
76 
77  Binding {
78  target: scopeView.scope
79  property: "searchQuery"
80  value: pageHeader.searchQuery
81  when: isCurrent
82  }
83 
84  Binding {
85  target: pageHeader
86  property: "searchQuery"
87  value: scopeView.scope ? scopeView.scope.searchQuery : ""
88  when: isCurrent
89  }
90 
91  Connections {
92  target: panel
93  onSearchClicked: if (isCurrent) {
94  pageHeader.triggerSearch()
95  categoryView.showHeader()
96  }
97  }
98 
99  Connections {
100  target: scopeView.scope
101  onShowDash: previewListView.open = false;
102  onHideDash: previewListView.open = false;
103  }
104 
105  ScopeListView {
106  id: categoryView
107  objectName: "categoryListView"
108  anchors.fill: parent
109  model: scopeView.categories
110  forceNoClip: previewListView.open
111 
112  property string expandedCategoryId: ""
113 
114  onContentYChanged: pageHeader.positionRealHeader();
115  onOriginYChanged: pageHeader.positionRealHeader();
116  onContentHeightChanged: pageHeader.positionRealHeader();
117 
118  delegate: ListItems.Base {
119  id: baseItem
120  objectName: "dashCategory" + category
121  highlightWhenPressed: false
122  showDivider: false
123 
124  readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
125  readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filtered : true
126  readonly property string category: categoryId
127  readonly property var item: rendererLoader.item
128 
129  CardTool {
130  id: cardTool
131 
132  count: results.count
133  template: model.renderer
134  components: model.components
135  viewWidth: parent.width
136  }
137 
138  Loader {
139  id: rendererLoader
140  anchors {
141  top: parent.top
142  left: parent.left
143  right: parent.right
144  }
145 
146  source: {
147  switch (cardTool.categoryLayout) {
148  case "carousel": return "CardCarousel.qml";
149  case "running-apps": return "Apps/RunningApplicationsGrid.qml";
150  case "grid":
151  default: return "CardFilterGrid.qml";
152  }
153  }
154 
155  onLoaded: {
156  if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
157  item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
158  scopeView.enableHeightBehaviorOnNextCreation = false;
159  }
160  if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
161  // TODO: this is still a kludge :D Ideally add some kind of hook so that we
162  // can do this from DashApps.qml or think a better way that needs no special casing
163  item.model = Qt.binding(function() { return runningApps; })
164  item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
165  } else {
166  item.model = Qt.binding(function() { return results })
167  }
168  item.objectName = Qt.binding(function() { return categoryId })
169  if (item.expandable) {
170  var shouldFilter = categoryId != categoryView.expandedCategoryId;
171  item.setFilter(shouldFilter, false /*animate*/);
172  }
173  updateDelegateCreationRange();
174  item.cardTool = cardTool;
175  }
176 
177  Component.onDestruction: {
178  if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
179  scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
180  }
181  }
182 
183  Connections {
184  target: rendererLoader.item
185  onClicked: {
186  if (scopeView.scope.id === "scopes" || (scopeView.scope.id == "clickscope" && categoryId == "local")) {
187  // TODO Technically it is possible that calling activate() will make the scope emit
188  // previewRequested so that we show a preview but there's no scope that does that yet
189  // so it's not implemented
190  var item = target.model.get(index);
191  scopeView.scope.activate(item.result)
192  } else {
193  previewListView.model = target.model;
194  previewListView.currentIndex = -1
195  previewListView.currentIndex = index;
196  previewListView.open = true
197  }
198  }
199  onPressAndHold: {
200  previewListView.model = target.model;
201  previewListView.currentIndex = -1
202  previewListView.currentIndex = index;
203  previewListView.open = true
204  }
205  }
206  Connections {
207  target: categoryView
208  onExpandedCategoryIdChanged: {
209  collapseAllButExpandedCategory();
210  }
211  function collapseAllButExpandedCategory() {
212  var item = rendererLoader.item;
213  if (item.expandable) {
214  var shouldFilter = categoryId != categoryView.expandedCategoryId;
215  if (shouldFilter != item.filter) {
216  // If the filter animation will be seen start it, otherwise, just flip the switch
217  var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
218  var growingVisible = !shouldFilter && y + height < categoryView.height;
219  if (!previewListView.open || !shouldFilter) {
220  var animate = shrinkingVisible || growingVisible;
221  item.setFilter(shouldFilter, animate)
222  if (!shouldFilter && !previewListView.open) {
223  categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
224  }
225  }
226  }
227  }
228  }
229  onOriginYChanged: rendererLoader.updateDelegateCreationRange();
230  onContentYChanged: rendererLoader.updateDelegateCreationRange();
231  onHeightChanged: rendererLoader.updateDelegateCreationRange();
232  onContentHeightChanged: rendererLoader.updateDelegateCreationRange();
233  }
234 
235  function updateDelegateCreationRange() {
236  if (categoryView.moving) {
237  // Do not update the range if we are overshooting up or down, since we'll come back
238  // to the stable position and delete/create items without any reason
239  if (categoryView.contentY < categoryView.originY) {
240  return;
241  } else if (categoryView.contentHeight - categoryView.originY > categoryView.height &&
242  categoryView.contentY + categoryView.height > categoryView.contentHeight) {
243  return;
244  }
245  }
246 
247  if (item && item.hasOwnProperty("delegateCreationBegin")) {
248  if (baseItem.y + baseItem.height <= 0) {
249  // Not visible (item at top of the list viewport)
250  item.delegateCreationBegin = item.originY + baseItem.height
251  item.delegateCreationEnd = item.originY + baseItem.height
252  } else if (baseItem.y >= categoryView.height) {
253  // Not visible (item at bottom of the list viewport)
254  item.delegateCreationBegin = item.originY
255  item.delegateCreationEnd = item.originY
256  } else {
257  item.delegateCreationBegin = item.originY + Math.max(-baseItem.y, 0)
258  item.delegateCreationEnd = item.originY + Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
259  }
260  }
261  }
262 
263  Image {
264  visible: index != 0
265  anchors {
266  top: parent.top
267  left: parent.left
268  right: parent.right
269  }
270  fillMode: Image.Stretch
271  source: "graphics/dash_divider_top_lightgrad.png"
272  z: -1
273  }
274 
275  Image {
276  // FIXME Should not rely on model.count but view.count, but ListViewWithPageHeader doesn't expose it yet.
277  visible: index != categoryView.model.count - 1
278  anchors {
279  bottom: parent.bottom
280  left: parent.left
281  right: parent.right
282  }
283  fillMode: Image.Stretch
284  source: "graphics/dash_divider_top_darkgrad.png"
285  z: -1
286  }
287  }
288 
289  onHeightChanged: rendererLoader.updateDelegateCreationRange();
290  onYChanged: rendererLoader.updateDelegateCreationRange();
291  }
292 
293  sectionProperty: "name"
294  sectionDelegate: ListItems.Header {
295  objectName: "dashSectionHeader" + (delegate ? delegate.category : "")
296  property var delegate: categoryView.item(delegateIndex)
297  width: categoryView.width
298  text: section
299  image: {
300  if (delegate && delegate.expandable)
301  return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
302  return "";
303  }
304  onClicked: {
305  if (categoryView.expandedCategoryId != delegate.category)
306  categoryView.expandedCategoryId = delegate.category;
307  else
308  categoryView.expandedCategoryId = "";
309  }
310  }
311  pageHeader: Item {
312  implicitHeight: scopeView.tabBarHeight
313  onHeightChanged: {
314  if (scopeView.pageHeader && scopeView.isCurrent) {
315  scopeView.pageHeader.height = height;
316  }
317  }
318  onYChanged: positionRealHeader();
319 
320  function positionRealHeader() {
321  if (scopeView.pageHeader && scopeView.isCurrent) {
322  scopeView.pageHeader.y = y + parent.y;
323  }
324  }
325  }
326  }
327 }
Tool for introspecting Card properties.
Definition: CardTool.qml:25
int count
Number of cards.
Definition: CardTool.qml:30