on bec1ca4 help sign in
unity8/Dash/GenericScopeView.qml
331 lines 14 kB history raw
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 */
17import QtQuick 2.0
18import Ubuntu.Components 0.1
19import "../Components"
20import "../Components/ListItems" as ListItems
22ScopeView {
23 id: scopeView
24 property bool enableHeightBehaviorOnNextCreation: false
25 property var categoryView: categoryView
27 moving: categoryView.moving
29 onIsCurrentChanged: {
30 pageHeader.resetSearch();
31 previewListView.open = false;
32 }
34 onMovementStarted: categoryView.showHeader()
36 onPositionedAtBeginning: categoryView.positionAtBeginning()
38 Binding {
39 target: scopeView.scope
40 property: "searchQuery"
41 value: pageHeader.searchQuery
42 when: isCurrent
43 }
45 Binding {
46 target: pageHeader
47 property: "searchQuery"
48 value: scopeView.scope.searchQuery
49 when: isCurrent
50 }
52 Connections {
53 target: panel
54 onSearchClicked: if (isCurrent) {
55 pageHeader.triggerSearch()
56 categoryView.showHeader()
57 }
58 }
60 Connections {
61 target: scopeView.scope
62 onShowDash: previewListView.open = false;
63 onHideDash: previewListView.open = false;
64 }
66 ScopeListView {
67 id: categoryView
68 objectName: "categoryListView"
69 anchors.fill: parent
70 model: scopeView.categories
71 forceNoClip: previewListView.onScreen
73 onAtYEndChanged: if (atYEnd) endReached()
74 onMovingChanged: if (moving && atYEnd) endReached()
76 property string expandedCategoryId: ""
77 signal correctExpandedCategory();
79 onContentYChanged: pageHeader.positionRealHeader();
80 onOriginYChanged: pageHeader.positionRealHeader();
82 Behavior on contentY {
83 enabled: previewListView.open
84 UbuntuNumberAnimation {}
85 }
87 delegate: ListItems.Base {
88 id: baseItem
89 highlightWhenPressed: false
91 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
92 readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
93 readonly property string category: categoryId
95 Loader {
96 id: rendererLoader
97 anchors {
98 top: parent.top
99 left: parent.left
100 right: parent.right
101 }
103 source: getRenderer(model.renderer, model.contentType, model.rendererHint)
105 onLoaded: {
106 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
107 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
108 scopeView.enableHeightBehaviorOnNextCreation = false;
109 }
110 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
111 // TODO: the running apps grid doesn't support standard scope results model yet
112 item.firstModel = Qt.binding(function() { return results.firstModel })
113 item.secondModel = Qt.binding(function() { return results.secondModel })
114 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
115 } else {
116 item.model = Qt.binding(function() { return results })
117 }
118 item.objectName = Qt.binding(function() { return categoryId })
119 if (item.expandable) {
120 var shouldFilter = categoryId != categoryView.expandedCategoryId;
121 if (shouldFilter != item.filter) {
122 item.filter = shouldFilter;
123 }
124 }
125 updateDelegateCreationRange();
126 }
128 Component.onDestruction: {
129 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
130 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
131 }
132 }
134 Connections {
135 target: rendererLoader.item
136 onClicked: {
137 // Prepare the preview in case activate() triggers a preview only
138 openEffect.positionPx = mapToItem(categoryView, 0, itemY).y
139 previewListView.categoryId = categoryId
140 previewListView.categoryDelegate = rendererLoader.item
141 previewListView.model = model;
142 previewListView.init = true;
143 previewListView.currentIndex = index;
145 var item = model.get(index);
147 if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
148 || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
149 scopeView.scope.activate(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
150 item.comment, item.dndUri, item.metadata)
151 } else {
152 previewListView.open = true
154 scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
155 item.comment, item.dndUri, item.metadata)
156 }
157 }
158 onPressAndHold: {
159 openEffect.positionPx = mapToItem(categoryView, 0, itemY).y
160 previewListView.categoryId = categoryId
161 previewListView.categoryDelegate = rendererLoader.item
162 previewListView.model = model;
163 previewListView.init = true;
164 previewListView.currentIndex = index;
165 previewListView.open = true
167 var item = model.get(index)
168 scopeView.scope.preview( item.uri, item.icon, item.category, 0, item.mimetype, item.title,
169 item.comment, item.dndUri, item.metadata)
170 }
171 }
172 Connections {
173 target: categoryView
174 onExpandedCategoryIdChanged: {
175 collapseAllButExpandedCategory();
176 }
177 onCorrectExpandedCategory: {
178 collapseAllButExpandedCategory();
179 }
180 function collapseAllButExpandedCategory() {
181 var item = rendererLoader.item;
182 if (item.expandable) {
183 var shouldFilter = categoryId != categoryView.expandedCategoryId;
184 if (shouldFilter != item.filter) {
185 // If the filter animation will be seen start it, otherwise, just flip the switch
186 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
187 var growingVisible = !shouldFilter && y + height < categoryView.height;
188 if (!previewListView.open || !shouldFilter) {
189 if (shrinkingVisible || growingVisible) {
190 item.startFilterAnimation(shouldFilter)
191 } else {
192 item.filter = shouldFilter;
193 }
194 if (!shouldFilter && !previewListView.open) {
195 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
196 }
197 }
198 }
199 }
200 }
201 onContentYChanged: rendererLoader.updateDelegateCreationRange();
202 onHeightChanged: rendererLoader.updateDelegateCreationRange();
203 }
205 function updateDelegateCreationRange() {
206 // Do not update the range if we are overshooting up or down, since we'll come back
207 // to the stable position and delete/create items without any reason
208 if (categoryView.contentY < categoryView.originY) {
209 return;
210 } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) {
211 return;
212 }
214 if (item.hasOwnProperty("delegateCreationBegin")) {
215 if (baseItem.y + baseItem.height <= 0) {
216 // Not visible (item at top of the list)
217 item.delegateCreationBegin = baseItem.height
218 item.delegateCreationEnd = baseItem.height
219 } else if (baseItem.y >= categoryView.height) {
220 // Not visible (item at bottom of the list)
221 item.delegateCreationBegin = 0
222 item.delegateCreationEnd = 0
223 } else {
224 item.delegateCreationBegin = Math.max(-baseItem.y, 0)
225 item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
226 }
227 }
228 }
229 }
230 }
232 sectionProperty: "name"
233 sectionDelegate: ListItems.Header {
234 property var delegate: categoryView.item(delegateIndex)
235 width: categoryView.width
236 text: section
237 image: {
238 if (delegate && delegate.expandable)
239 return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
240 return "";
241 }
242 onClicked: {
243 if (categoryView.expandedCategoryId != delegate.category)
244 categoryView.expandedCategoryId = delegate.category;
245 else
246 categoryView.expandedCategoryId = "";
247 }
248 }
249 pageHeader: Item {
250 implicitHeight: scopeView.tabBarHeight
251 onHeightChanged: scopeView.headerHeightChanged(height)
252 onYChanged: positionRealHeader();
254 function positionRealHeader() {
255 scopeView.headerPositionChanged(y + parent.y)
256 }
257 }
258 }
260 function getDefaultRendererId(contentType) {
261 switch (contentType) {
262 default: return "grid";
263 }
264 }
266 function getRenderer(rendererId, contentType, rendererHint) {
267 if (rendererId == "default") {
268 rendererId = getDefaultRendererId(contentType);
269 }
270 switch (rendererId) {
271 case "grid": {
272 switch (contentType) {
273 case "video": return "Video/VideoFilterGrid.qml";
274 case "music": return "Music/MusicFilterGrid.qml";
275 case "apps": {
276 if (rendererHint == "toggled")
277 return "Apps/DashPluginFilterGrid.qml";
278 else
279 return "Generic/GenericFilterGrid.qml";
280 }
281 case "weather": return "Generic/WeatherFilterGrid.qml";
282 default: return "Generic/GenericFilterGrid.qml";
283 }
284 }
285 case "carousel": {
286 switch (contentType) {
287 case "music": return "Music/MusicCarousel.qml";
288 default: return "Generic/GenericCarousel.qml";
289 }
290 }
291 case "special": {
292 switch (contentType) {
293 case "apps": return "Apps/RunningApplicationsGrid.qml";
294 default: return "Generic/GenericFilterGrid.qml";
295 }
296 }
297 default: return "Generic/GenericFilterGrid.qml";
298 }
299 }
301 Connections {
302 target: scopeView.scope
303 onPreviewReady: {
304 if (previewListView.init) {
305 // Preview was triggered because of a click on the item. Need to expand now.
306 if (!previewListView.open) {
307 previewListView.open = true
308 }
310 var index = previewListView.currentIndex
311 previewListView.currentIndex = -1
312 previewListView.currentIndex = index
313 previewListView.init = false
314 }
315 previewListView.currentItem.previewData = preview
316 }
317 }
319 // TODO: Move as InverseMouseArea to DashPreview
320 MouseArea {
321 objectName: "closePreviewMouseArea"
322 enabled: previewListView.onScreen
323 anchors {
324 fill: parent
325 topMargin: openEffect.bottomGapPx
326 }
327 onClicked: {
328 previewListView.open = false;
329 }
330 }