2 * Copyright (C) 2013 Canonical, Ltd.
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.
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.
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/>.
18import Ubuntu.Components 0.1
22import "../Components/ListItems" as ListItems
28 property SortFilterProxyModel categories: categoryFilter
29 property bool isCurrent
30 property alias moving: categoryView.moving
31 property int tabBarHeight: 0
32 property PageHeader pageHeader: null
33 property OpenEffect openEffect: null
34 property Item previewListView: null
37 signal movementStarted
38 signal positionedAtBeginning
40 property bool enableHeightBehaviorOnNextCreation: false
41 property var categoryView: categoryView
43 // FIXME delay the search so that daemons have time to settle, note that
44 // removing this will break ScopeView::test_changeScope
48 scope.activateApplication.connect(activateApp);
52 function activateApp(appId) {
53 shell.activateApplication(appId);
65 onTriggered: scope.searchQuery = ""
68 SortFilterProxyModel {
70 model: scope ? scope.categories : null
71 dynamicSortFilter: true
72 filterRole: Categories.RoleCount
78 pageHeader.resetSearch();
79 previewListView.open = false;
82 onMovementStarted: categoryView.showHeader()
84 onPositionedAtBeginning: categoryView.positionAtBeginning()
87 target: scopeView.scope
88 property: "searchQuery"
89 value: pageHeader.searchQuery
95 property: "searchQuery"
96 value: scopeView.scope.searchQuery
102 onSearchClicked: if (isCurrent) {
103 pageHeader.triggerSearch()
104 categoryView.showHeader()
109 target: scopeView.scope
110 onShowDash: previewListView.open = false;
111 onHideDash: previewListView.open = false;
116 objectName: "categoryListView"
118 model: scopeView.categories
119 forceNoClip: previewListView.onScreen
121 onAtYEndChanged: if (atYEnd) endReached()
122 onMovingChanged: if (moving && atYEnd) endReached()
124 property string expandedCategoryId: ""
125 signal correctExpandedCategory();
127 onContentYChanged: pageHeader.positionRealHeader();
128 onOriginYChanged: pageHeader.positionRealHeader();
129 onContentHeightChanged: pageHeader.positionRealHeader();
131 Behavior on contentY {
132 enabled: previewListView.open
133 UbuntuNumberAnimation {}
136 delegate: ListItems.Base {
138 objectName: "dashCategory" + category
139 highlightWhenPressed: false
141 readonly property bool expandable: rendererLoader.item ? rendererLoader.item.expandable : false
142 readonly property bool filtered: rendererLoader.item ? rendererLoader.item.filter : true
143 readonly property string category: categoryId
144 readonly property var item: rendererLoader.item
154 source: getRenderer(model.renderer, results)
157 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
158 item.enableHeightBehavior = scopeView.enableHeightBehaviorOnNextCreation;
159 scopeView.enableHeightBehaviorOnNextCreation = false;
161 if (source.toString().indexOf("Apps/RunningApplicationsGrid.qml") != -1) {
162 // TODO: the running apps grid doesn't support standard scope results model yet
163 item.firstModel = Qt.binding(function() { return results.firstModel })
164 item.secondModel = Qt.binding(function() { return results.secondModel })
165 item.canEnableTerminationMode = Qt.binding(function() { return scopeView.isCurrent })
167 item.model = Qt.binding(function() { return results })
169 item.objectName = Qt.binding(function() { return categoryId })
170 if (item.expandable) {
171 var shouldFilter = categoryId != categoryView.expandedCategoryId;
172 if (shouldFilter != item.filter) {
173 item.filter = shouldFilter;
176 updateDelegateCreationRange();
177 // FIXME: should be "template", not "renderer"
178 item.template = Qt.binding(function() { return model.renderer });
179 item.components = Qt.binding(function() { return model.components });
182 Component.onDestruction: {
183 if (item.enableHeightBehavior !== undefined && item.enableHeightBehaviorOnNextCreation !== undefined) {
184 scopeView.enableHeightBehaviorOnNextCreation = item.enableHeightBehaviorOnNextCreation;
189 target: rendererLoader.item
191 // Prepare the preview in case activate() triggers a preview only
192 openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
193 previewListView.categoryId = categoryId
194 previewListView.categoryDelegate = rendererLoader.item
195 previewListView.model = target.model;
196 previewListView.init = true;
197 previewListView.currentIndex = index;
199 var item = target.model.get(index);
201 if ((scopeView.scope.id == "applications.scope" && categoryId == "installed")
202 || (scopeView.scope.id == "home.scope" && categoryId == "applications.scope")) {
203 scopeView.scope.activate(item.result)
205 previewListView.open = true
207 scopeView.scope.preview(item.result)
211 openEffect.positionPx = Math.max(mapToItem(categoryView, 0, itemY).y, pageHeader.height + categoryView.stickyHeaderHeight);
212 previewListView.categoryId = categoryId
213 previewListView.categoryDelegate = rendererLoader.item
214 previewListView.model = target.model;
215 previewListView.init = true;
216 previewListView.currentIndex = index;
217 previewListView.open = true
219 var item = target.model.get(index)
220 scopeView.scope.preview(item.uri, item.icon, item.category, 0, item.mimetype, item.title,
221 item.comment, item.dndUri, item.metadata)
226 onExpandedCategoryIdChanged: {
227 collapseAllButExpandedCategory();
229 onCorrectExpandedCategory: {
230 collapseAllButExpandedCategory();
232 function collapseAllButExpandedCategory() {
233 var item = rendererLoader.item;
234 if (item.expandable) {
235 var shouldFilter = categoryId != categoryView.expandedCategoryId;
236 if (shouldFilter != item.filter) {
237 // If the filter animation will be seen start it, otherwise, just flip the switch
238 var shrinkingVisible = shouldFilter && y + item.collapsedHeight < categoryView.height;
239 var growingVisible = !shouldFilter && y + height < categoryView.height;
240 if (!previewListView.open || !shouldFilter) {
241 if (shrinkingVisible || growingVisible) {
242 item.startFilterAnimation(shouldFilter)
244 item.filter = shouldFilter;
246 if (!shouldFilter && !previewListView.open) {
247 categoryView.maximizeVisibleArea(index, item.uncollapsedHeight);
253 onOriginYChanged: rendererLoader.updateDelegateCreationRange();
254 onContentYChanged: rendererLoader.updateDelegateCreationRange();
255 onHeightChanged: rendererLoader.updateDelegateCreationRange();
256 onContentHeightChanged: rendererLoader.updateDelegateCreationRange();
259 function updateDelegateCreationRange() {
260 // Do not update the range if we are overshooting up or down, since we'll come back
261 // to the stable position and delete/create items without any reason
262 if (categoryView.contentY < categoryView.originY) {
264 } else if (categoryView.contentY + categoryView.height > categoryView.contentHeight) {
268 if (item && item.hasOwnProperty("delegateCreationBegin")) {
269 if (baseItem.y + baseItem.height <= 0) {
270 // Not visible (item at top of the list)
271 item.delegateCreationBegin = baseItem.height
272 item.delegateCreationEnd = baseItem.height
273 } else if (baseItem.y >= categoryView.height) {
274 // Not visible (item at bottom of the list)
275 item.delegateCreationBegin = 0
276 item.delegateCreationEnd = 0
278 item.delegateCreationBegin = Math.max(-baseItem.y, 0)
279 item.delegateCreationEnd = Math.min(categoryView.height + item.delegateCreationBegin, baseItem.height)
285 onHeightChanged: rendererLoader.updateDelegateCreationRange();
286 onYChanged: rendererLoader.updateDelegateCreationRange();
289 sectionProperty: "name"
290 sectionDelegate: ListItems.Header {
291 objectName: "dashSectionHeader" + delegate.category
292 property var delegate: categoryView.item(delegateIndex)
293 width: categoryView.width
296 if (delegate && delegate.expandable)
297 return delegate.filtered ? "graphics/header_handlearrow.png" : "graphics/header_handlearrow2.png"
301 if (categoryView.expandedCategoryId != delegate.category)
302 categoryView.expandedCategoryId = delegate.category;
304 categoryView.expandedCategoryId = "";
308 implicitHeight: scopeView.tabBarHeight
310 if (scopeView.pageHeader) {
311 scopeView.pageHeader.height = height;
314 onYChanged: positionRealHeader();
316 function positionRealHeader() {
317 if (scopeView.pageHeader) {
318 scopeView.pageHeader.y = y + parent.y;
324 function getRenderer(template, results) {
325 var layout = template["category-layout"];
327 if (layout === "carousel") {
328 // TODO: Selectively disable carousel, 4 is fixed for now, should change depending on form factor
329 if (results.count <= 4) layout = "grid";
332 case "carousel": return "CardCarousel.qml";
334 default: return "CardFilterGrid.qml";
339 target: scopeView.scope
341 if (previewListView.init) {
342 // Preview was triggered because of a click on the item. Need to expand now.
343 if (!previewListView.open) {
344 previewListView.open = true
347 var index = previewListView.currentIndex
348 previewListView.currentIndex = -1
349 previewListView.currentIndex = index
350 previewListView.init = false
352 previewListView.currentItem.previewData = preview
356 // TODO: Move as InverseMouseArea to DashPreview
358 objectName: "closePreviewMouseArea"
359 enabled: previewListView.onScreen
362 topMargin: openEffect.bottomGapPx
365 previewListView.open = false;
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list