Unity 8
ScopesList.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 import "../Components"
21 
22 Item {
23  id: root
24 
25  property alias scopesListFlickable: scopesListFlickable
26 
27  // Properties set by parent
28  property var scope: null
29 
30  // Properties used by parent
31  readonly property bool processing: scope ? (scope.searchInProgress || scope.activationInProgress) : false
32 
33  // Signals
34  signal backClicked()
35  signal storeClicked()
36  signal requestFavorite(string scopeId, bool favorite)
37  signal requestFavoriteMoveTo(string scopeId, int index)
38  signal requestRestore(string scopeId)
39 
40  Item {
41  id: autoscroller
42 
43  property bool dragging: false
44  property var dragItem: new Object();
45 
46  readonly property bool fuzzyAtYEnd: {
47  var contentHeight = root.scopesListFlickable.contentHeight
48  var contentY = root.scopesListFlickable.contentY
49  var dragItemHeight = dragItem ? autoscroller.dragItem.height : 0
50  var flickableHeight = root.scopesListFlickable.height
51 
52  if (!dragItem) {
53  return true;
54  } else {
55  return contentY >= (contentHeight - flickableHeight) - dragItemHeight
56  }
57  }
58 
59  readonly property real bottomBoundary: {
60  var contentHeight = root.scopesListFlickable.contentHeight
61  var contentY = root.scopesListFlickable.contentY
62  var dragItemHeight = dragItem ? autoscroller.dragItem.height : 0
63  var heightRatio = root.scopesListFlickable.visibleArea.heightRatio
64 
65  if (!dragItem) {
66  return true;
67  } else {
68  return (heightRatio * contentHeight) -
69  (1.5 * dragItemHeight) + contentY
70  }
71  }
72 
73  readonly property int delayMs: 32
74  readonly property real topBoundary: dragItem ? root.scopesListFlickable.contentY + (.5 * dragItem.height) : 0
75 
76  visible: false
77  readonly property real maxStep: units.dp(10)
78  function stepSize(scrollingUp) {
79  var delta, step;
80  if (scrollingUp) {
81  delta = dragItem.y - topBoundary;
82  delta /= (1.5 * dragItem.height);
83  } else {
84  delta = dragItem.y - bottomBoundary;
85  delta /= (1.5 * dragItem.height);
86  }
87 
88  step = Math.abs(delta) * autoscroller.maxStep
89  return Math.ceil(step);
90  }
91 
92 
93  Timer {
94  interval: autoscroller.delayMs
95  running: autoscroller.dragItem ? (autoscroller.dragging &&
96  autoscroller.dragItem.y < autoscroller.topBoundary &&
97  !root.scopesListFlickable.atYBeginning) : false
98  repeat: true
99  onTriggered: {
100  root.scopesListFlickable.contentY -= autoscroller.stepSize(true);
101  autoscroller.dragItem.y -= autoscroller.stepSize(true);
102  }
103  }
104 
105  Timer {
106  interval: autoscroller.delayMs
107  running: autoscroller.dragItem ? (autoscroller.dragging &&
108  autoscroller.dragItem.y >= autoscroller.bottomBoundary &&
109  !autoscroller.fuzzyAtYEnd) : false
110  repeat: true
111  onTriggered: {
112  root.scopesListFlickable.contentY += autoscroller.stepSize(false);
113  autoscroller.dragItem.y += autoscroller.stepSize(false);
114  }
115  }
116  }
117 
118  function autoscroll(dragging, dragItem) {
119  if (dragging) {
120  autoscroller.dragItem = dragItem
121  autoscroller.dragging = true;
122  } else {
123  autoscroller.dragItem = null;
124  autoscroller.dragging = false
125  }
126  }
127 
128  state: "browse"
129 
130  property var scopeStyle: ScopeStyle {}
131 
132  onStateChanged: {
133  if (state == "edit") {
134  // As per design entering edit mode clears the possible existing search
135  header.resetSearch(false /* false == unfocus */);
136  }
137  }
138 
139  DashBackground {
140  anchors.fill: parent
141  }
142 
143  DashPageHeader {
144  id: header
145  objectName: "pageHeader"
146  title: i18n.tr("Manage")
147  width: parent.width
148  clip: true
149  showBackButton: true
150  backIsClose: root.state == "edit"
151  storeEntryEnabled: root.state == "browse"
152  searchEntryEnabled: false
153  scopeStyle: root.scopeStyle
154  onBackClicked: {
155  if (backIsClose) {
156  root.state = "browse"
157  } else {
158  root.backClicked()
159  }
160  }
161  onStoreClicked: root.storeClicked();
162  z: 1
163  }
164 
165  ListView {
166  id: scopesListFlickable
167  objectName: "scopesListFlickable"
168  anchors {
169  top: header.bottom
170  bottom: parent.bottom
171  left: parent.left
172  right: parent.right
173  }
174  clip: true
175  model: scope ? scope.categories : null
176  delegate: Loader {
177  asynchronous: true
178  width: root.width
179  active: results.count > 0
180  visible: active
181  sourceComponent: ScopesListCategory {
182  objectName: "scopesListCategory" + categoryId
183 
184  model: results
185 
186  title: {
187  if (isFavoritesFeed) return i18n.tr("Home");
188  else if (isAlsoInstalled) return i18n.tr("Also installed");
189  else return name;
190  }
191 
192  editMode: root.state == "edit"
193  scopeStyle: root.scopeStyle
194  isFavoritesFeed: categoryId == "favorites"
195  isAlsoInstalled: categoryId == "other"
196 
197  onItemDragging: autoscroll(dragging, dragItem);
198  onRequestFavorite: root.requestFavorite(scopeId, favorite);
199  onRequestEditMode: root.state = "edit";
200  onRequestScopeMoveTo: root.requestFavoriteMoveTo(scopeId, index);
201  onRequestActivate: root.scope.activate(result, categoryId);
202  onRequestRestore: root.requestRestore(scopeId);
203  }
204  }
205  }
206 }