Unity 8
DashContent.qml
1 /*
2  * Copyright (C) 2013, 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.0
18 import Ubuntu.Components 0.1
19 import Unity 0.2
20 import Utils 0.1
21 import "../Components"
22 
23 Item {
24  id: dashContent
25 
26  property bool forceNonInteractive: false
27  property alias scopes: dashContentList.model
28  property alias currentIndex: dashContentList.currentIndex
29  property int workaroundRestoreIndex: -1
30  readonly property string currentScopeId: dashContentList.currentItem ? dashContentList.currentItem.scopeId : ""
31  readonly property var currentScope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
32  readonly property bool subPageShown: dashContentList.currentItem && dashContentList.currentItem.item ?
33  dashContentList.currentItem.item.subPageShown : false
34  readonly property bool processing: dashContentList.currentItem && dashContentList.currentItem.item
35  && dashContentList.currentItem.item.processing || false
36  readonly property bool pageHeaderTotallyVisible: dashContentList.currentItem && dashContentList.currentItem.item
37  && dashContentList.currentItem.item.pageHeaderTotallyVisible || false
38 
39  signal scopeLoaded(string scopeId)
40  signal gotoScope(string scopeId)
41  signal openScope(var scope)
42  signal closePreview()
43 
44  // If we set the current scope index before the scopes have been added,
45  // then we need to wait until the loaded signals gets emitted from the scopes
46  property var set_current_index: undefined
47  Connections {
48  target: scopes
49  onLoadedChanged: {
50  if (scopes.loaded && set_current_index != undefined) {
51  setCurrentScopeAtIndex(set_current_index[0], set_current_index[1], set_current_index[2]);
52  set_current_index = undefined;
53  }
54  }
55  onRowsMoved: {
56  // FIXME This is to workaround a Qt bug with the model moving the current item
57  // when the list is ListView.SnapOneItem and ListView.StrictlyEnforceRange
58  // together with the code in Dash.qml
59  if (row == dashContentList.currentIndex || start == dashContentList.currentIndex) {
60  dashContent.workaroundRestoreIndex = dashContentList.currentIndex;
61  dashContentList.currentIndex = -1;
62  }
63  }
64  }
65 
66  Connections {
67  target: UriHandler
68  onOpened: dashContentList.currentItem.theScope.performQuery(uris[0])
69  }
70 
71  function setCurrentScopeAtIndex(index, animate, reset) {
72  // if the scopes haven't loaded yet, then wait until they are.
73  if (!scopes.loaded) {
74  set_current_index = [ index, animate, reset ]
75  return;
76  }
77 
78  var storedMoveDuration = dashContentList.highlightMoveDuration
79  var storedMoveSpeed = dashContentList.highlightMoveVelocity
80  if (!animate) {
81  dashContentList.highlightMoveVelocity = units.gu(4167)
82  dashContentList.highlightMoveDuration = 0
83  }
84 
85  set_current_index = undefined;
86 
87  if (dashContentList.count > index)
88  {
89  dashContentList.currentIndex = index
90 
91  if (reset) {
92  dashContentList.currentItem.item.positionAtBeginning()
93  }
94  }
95 
96  if (!animate) {
97  dashContentList.highlightMoveDuration = storedMoveDuration
98  dashContentList.highlightMoveVelocity = storedMoveSpeed
99  }
100  }
101 
102  Item {
103  id: dashContentListHolder
104 
105  anchors.fill: parent
106 
107  DashBackground {
108  anchors.fill: parent
109  }
110 
111  ListView {
112  id: dashContentList
113  objectName: "dashContentList"
114 
115  interactive: !dashContent.forceNonInteractive && dashContent.scopes.loaded && currentItem
116  && !currentItem.moving && !currentItem.navigationDisableParentInteractive && !currentItem.subPageShown
117  anchors.fill: parent
118  orientation: ListView.Horizontal
119  boundsBehavior: Flickable.DragAndOvershootBounds
120  flickDeceleration: units.gu(625)
121  maximumFlickVelocity: width * 5
122  snapMode: ListView.SnapOneItem
123  highlightMoveDuration: 250
124  highlightRangeMode: ListView.StrictlyEnforceRange
125  // TODO Investigate if we can switch to a smaller cache buffer when/if UbuntuShape gets more performant
126  // 1073741823 is s^30 -1. A quite big number so that you have "infinite" cache, but not so
127  // big so that if you add if with itself you're outside the 2^31 int range
128  cacheBuffer: 1073741823
129  onMovementStarted: currentItem.item.showHeader();
130  clip: parent.x != 0
131 
132  // TODO QTBUG-40846 and QTBUG-40848
133  // The remove transition doesn't happen when removing the last item
134  // And can't work around it because index is reset to -1 regardless of
135  // ListView.delayRemove
136 
137  remove: Transition {
138  SequentialAnimation {
139  PropertyAction { property: "layer.enabled"; value: true }
140  PropertyAction { property: "ListView.delayRemove"; value: true }
141  ParallelAnimation {
142  PropertyAnimation { properties: "scale"; to: 0.25; duration: UbuntuAnimation.SnapDuration }
143  PropertyAnimation { properties: "y"; to: dashContent.height; duration: UbuntuAnimation.SnapDuration }
144  }
145  PropertyAction { property: "ListView.delayRemove"; value: false }
146  }
147  }
148  removeDisplaced: Transition {
149  PropertyAnimation { property: "x"; duration: UbuntuAnimation.SnapDecision }
150  }
151 
152  // If the number of items is less than the current index, then need to reset to another item.
153  onCountChanged: {
154  if (count > 0) {
155  if (currentIndex >= count) {
156  dashContent.setCurrentScopeAtIndex(count-1, true, true)
157  } else if (currentIndex < 0) {
158  // setting currentIndex directly, cause we don't want to loose set_current_index
159  dashContentList.currentIndex = 0
160  }
161  }
162  }
163 
164  delegate:
165  Loader {
166  id: loader
167  width: ListView.view.width
168  height: ListView.view.height
169  opacity: { // hide delegate if offscreen
170  var xPositionRelativetoView = ListView.view.contentX - x
171  return (xPositionRelativetoView > -width && xPositionRelativetoView < width) ? 1 : 0
172  }
173  asynchronous: true
174  source: "GenericScopeView.qml"
175  objectName: "scopeLoader" + index
176 
177  readonly property bool moving: item ? item.moving : false
178  readonly property bool navigationDisableParentInteractive: item ? item.navigationDisableParentInteractive : false
179  readonly property bool subPageShown: item ? item.subPageShown : false
180  readonly property var categoryView: item ? item.categoryView : null
181  readonly property var theScope: scope
182 
183  // these are needed for autopilot tests
184  readonly property string scopeId: scope.id
185  readonly property bool isCurrent: ListView.isCurrentItem
186  readonly property bool isLoaded: status == Loader.Ready
187 
188  onLoaded: {
189  item.objectName = scope.id
190  item.scope = Qt.binding(function() { return scope })
191  item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
192  dashContent.scopeLoaded(item.scope.id)
193  item.paginationCount = Qt.binding(function() { return dashContentList.count } )
194  item.paginationIndex = Qt.binding(function() { return dashContentList.currentIndex } )
195  item.visibleToParent = Qt.binding(function() { return loader.opacity != 0 });
196  item.holdingList = dashContentList;
197  item.forceNonInteractive = Qt.binding(function() { return dashContent.forceNonInteractive } )
198  }
199  Connections {
200  target: isCurrent ? scope : null
201  onGotoScope: {
202  // Note here scopeId is the signal parameter and not the loader property
203  dashContent.gotoScope(scopeId);
204  }
205  onOpenScope: {
206  dashContent.openScope(scope);
207  }
208  }
209  Connections {
210  target: dashContent
211  onClosePreview: if (item) item.closePreview()
212  }
213 
214  Component.onDestruction: active = false
215  }
216  }
217  }
218 }