2 * Copyright (C) 2014 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/>.
19 import Ubuntu.Components 0.1
20 import "../Components"
25 // Properties set by parent
26 property real progress: 0
27 property var scope: null
28 property int currentIndex: 0
29 property real scopeScale: 1
31 // Properties set and used by parent
32 property alias currentTab: tabBar.currentTab
34 // Properties used by parent
35 readonly property bool processing: searchResultsViewer.processing || tempScopeItem.processing || previewListView.processing
36 property bool growingDashFromPos: false
37 readonly property bool searching: scope && scope.searchQuery == ""
38 readonly property bool showingNonFavoriteScope: tempScopeItem.scope != null
39 readonly property var dashItemEater: {
40 if (!forceXYScalerEater && tabBar.currentTab == 0 && middleItems.count > 0) {
41 var loaderItem = middleItems.itemAt(0).item;
42 return loaderItem && loaderItem.currentItem ? loaderItem.currentItem : null;
44 return scopesOverviewXYScaler;
46 readonly property size allCardSize: {
47 if (middleItems.count > 1) {
48 var loaderItem = middleItems.itemAt(1).item;
50 var cardTool = loaderItem.cardTool;
51 return Qt.size(cardTool.cardWidth, cardTool.cardHeight);
57 // Internal properties
58 property bool forceXYScalerEater: false
61 signal favoriteSelected(var scopeId)
62 signal allFavoriteSelected(var scopeId)
63 signal searchSelected(var scopeId, var result, var pos, var size)
68 var itemPos = scopesOverviewXYScaler.restorePosition;
69 var itemSize = scopesOverviewXYScaler.restoreSize;
70 scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
72 scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
73 scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
75 scopesOverviewXYScaler.x = 0;
76 scopesOverviewXYScaler.y = 0;
78 scopesOverviewXYScaler.opacity = 0;
79 tempScopeItem.scope = scope;
80 middleItems.overrideOpacity = 0;
81 scopesOverviewXYScaler.scale = 1;
82 scopesOverviewXYScaler.x = 0;
83 scopesOverviewXYScaler.y = 0;
84 scopesOverviewXYScaler.opacity = 1;
87 if (tabBar.currentTab == 0) {
88 root.favoriteSelected(scopeId);
90 root.allFavoriteSelected(scopeId);
92 previewListView.open = false;
102 function closeTempScope() {
103 if (tempScopeItem.scope) {
104 root.scope.closeScope(tempScopeItem.scope);
105 tempScopeItem.scope = null;
106 tempScopeItem.backClicked()
110 function animateDashFromAll(scopeId) {
111 var currentScopePos = allScopeCardPosition(scopeId);
112 if (currentScopePos) {
113 showDashFromPos(currentScopePos, allCardSize);
115 console.log("Warning: Could not find Dash OverView All card position for scope", dashContent.currentScopeId);
119 function showDashFromPos(itemPos, itemSize) {
120 scopesOverviewXYScaler.scale = itemSize.width / scopesOverviewXYScaler.width;
121 scopesOverviewXYScaler.x = itemPos.x -(scopesOverviewXYScaler.width - scopesOverviewXYScaler.width * scopesOverviewXYScaler.scale) / 2;
122 scopesOverviewXYScaler.y = itemPos.y -(scopesOverviewXYScaler.height - scopesOverviewXYScaler.height * scopesOverviewXYScaler.scale) / 2;
123 scopesOverviewXYScaler.opacity = 0;
124 root.growingDashFromPos = true;
125 scopesOverviewXYScaler.scale = 1;
126 scopesOverviewXYScaler.x = 0;
127 scopesOverviewXYScaler.y = 0;
128 scopesOverviewXYScaler.opacity = 1;
131 function allScopeCardPosition(scopeId) {
132 if (middleItems.count > 1) {
133 var loaderItem = middleItems.itemAt(1).item;
135 var pos = loaderItem.scopeCardPosition(scopeId);
136 return loaderItem.mapToItem(null, pos.x, pos.y);
141 function ensureAllScopeVisible(scopeId) {
142 if (middleItems.count > 1) {
143 var loaderItem = middleItems.itemAt(1).item;
145 var pos = loaderItem.scopeCardPosition(scopeId);
146 loaderItem.contentY = Math.min(pos.y, loaderItem.contentHeight - loaderItem.height);
153 pageHeader.resetSearch();
154 pageHeader.unfocus(); // Shouldn't the previous call do this too?
159 id: overviewScopeStyle
160 style: { "foreground-color" : "white",
161 "background-color" : "transparent",
163 "background": "color:///transparent"
170 source: "graphics/dark_background.jpg"
175 onSearchQueryChanged: {
176 // Need this in order, otherwise something gets unhappy in rendering
177 // of the overlay in carousels because the parent of the dash dies for
178 // a moment, this way we make sure it's reparented first
179 // by forceXYScalerEater making dashItemEater return scopesOverviewXYScaler
180 // before we kill the previous parent by scope.searchQuery
181 root.forceXYScalerEater = true;
182 root.scope.searchQuery = pageHeader.searchQuery;
183 root.forceXYScalerEater = false;
189 property: "searchQuery"
190 value: scope ? scope.searchQuery : ""
194 id: scopesOverviewContent
195 x: previewListView.open ? -width : 0
196 Behavior on x { UbuntuNumberAnimation { } }
198 height: parent.height
202 objectName: "scopesOverviewPageHeader"
204 readonly property real yDisplacement: pageHeader.height + tabBar.height + tabBar.anchors.margins
207 if (root.progress < 0.5) {
208 return -yDisplacement;
210 return -yDisplacement + (root.progress - 0.5) * yDisplacement * 2;
215 title: i18n.tr("Manage Scopes")
216 scopeStyle: overviewScopeStyle
217 showSignatureLine: false
218 searchEntryEnabled: true
226 top: pageHeader.bottom
231 enabled: opacity == 1
232 opacity: !scope || scope.searchQuery == "" ? 1 : 0
233 Behavior on opacity { UbuntuNumberAnimation { } }
238 objectName: "scopesOverviewRepeater"
239 property real overrideOpacity: -1
240 model: scope && scope.searchQuery == "" ? scope.categories : null
243 objectName: "scopesOverviewRepeaterChild" + index
249 return root.height - pageHeader.height - tabBar.height - tabBar.anchors.margins - units.gu(2);
254 return root.width / scopeScale;
261 return (root.width - width) / 2;
267 bottom: scopesOverviewContent.bottom
270 scale: index == 0 ? scopeScale : 1
273 if (middleItems.overrideOpacity >= 0)
274 return middleItems.overrideOpacity;
276 if (tabBar.currentTab != index)
279 return index == 0 ? 1 : root.progress;
281 Behavior on opacity {
282 enabled: root.progress == 1
283 UbuntuNumberAnimation { }
285 enabled: opacity == 1
291 objectName: "cardTool"
293 template: model.renderer
294 components: model.components
295 viewWidth: parent.width
299 if (index == 0 && categoryId == "favorites") return "ScopesOverviewFavorites.qml";
300 else if (index == 1 && categoryId == "all") return "ScopesOverviewAll.qml";
305 item.model = Qt.binding(function() { return results; });
306 item.cardTool = cardTool;
308 item.scopeWidth = Qt.binding(function() { return root.width; });
309 item.scopeHeight = Qt.binding(function() { return root.height; });
310 item.appliedScale = Qt.binding(function() { return loader.scale });
311 item.currentIndex = Qt.binding(function() { return root.currentIndex });
312 } else if (index == 1) {
313 item.extraHeight = bottomBar.height;
320 pageHeader.unfocus();
321 if (tabBar.currentTab == 0) {
322 root.favoriteSelected(itemModel.scopeId);
324 var favoriteScopesItem = middleItems.itemAt(0).item;
325 var scopeIndex = favoriteScopesItem.model.scopeIndex(itemModel.scopeId);
326 if (scopeIndex >= 0) {
327 root.allFavoriteSelected(itemModel.scopeId);
329 // Will result in an openScope from root.scope
330 scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
331 scopesOverviewXYScaler.restoreSize = allCardSize;
332 root.scope.activate(result);
337 // Preview can call openScope so make sure restorePosition and restoreSize are set
338 scopesOverviewXYScaler.restorePosition = undefined;
339 scopesOverviewXYScaler.restoreSize = allCardSize;
341 previewListView.model = target.model;
342 previewListView.currentIndex = -1;
343 previewListView.currentIndex = index;
344 previewListView.open = true;
351 id: searchResultsViewer
352 objectName: "searchResultsViewer"
354 top: pageHeader.bottom
357 bottom: parent.bottom
359 scope: root.scope && root.scope.searchQuery != "" ? root.scope : null
360 scopeStyle: overviewScopeStyle
361 enabled: opacity == 1
362 showPageHeader: false
364 opacity: searchResultsViewer.scope ? 1 : 0
366 Behavior on opacity { UbuntuNumberAnimation { } }
368 function itemClicked(index, result, item, itemModel, resultsModel, limitedCategoryItemCount) {
369 pageHeader.unfocus();
370 pageHeader.closePopup();
371 if (itemModel.scopeId) {
372 // This can end up in openScope so save restorePosition and restoreSize
373 scopesOverviewXYScaler.restorePosition = item.mapToItem(null, 0, 0);
374 scopesOverviewXYScaler.restoreSize = Qt.size(item.width, item.height);
375 root.searchSelected(itemModel.scopeId, result, item.mapToItem(null, 0, 0), Qt.size(item.width, item.height));
377 // Not a scope, just activate it
378 searchResultsViewer.scope.activate(result);
382 function itemPressedAndHeld(index, itemModel, resultsModel, limitedCategoryItemCount) {
383 if (itemModel.uri.indexOf("scope://") === 0) {
384 // Preview can call openScope so make sure restorePosition and restoreSize are set
385 scopesOverviewXYScaler.restorePosition = undefined;
386 scopesOverviewXYScaler.restoreSize = allCardSize;
388 previewListView.model = resultsModel;
389 previewListView.currentIndex = -1;
390 previewListView.currentIndex = index;
391 previewListView.open = true;
398 objectName: "bottomBar"
402 enabled: opacity == 1
403 opacity: scope && scope.searchQuery == "" ? 1 : 0
404 Behavior on opacity { UbuntuNumberAnimation { } }
406 if (root.progress < 0.5) {
407 return parent.height;
409 return parent.height - (root.progress - 0.5) * height * 2;
414 // Just eat any other press since this parent is black opaque
419 objectName: "scopesOverviewDoneButton"
420 width: Math.max(label.width + units.gu(2), units.gu(10))
424 leftMargin: units.gu(2)
425 verticalCenter: parent.verticalCenter
429 border.color: "white"
430 border.width: units.dp(1)
432 color: parent.pressed ? Theme.palette.normal.baseText : "transparent"
436 anchors.centerIn: parent
437 text: i18n.tr("Done")
438 color: parent.pressed ? "black" : "white"
440 onClicked: root.done();
444 objectName: "scopesOverviewStoreButton"
445 width: Math.max(storeLabel.width, units.gu(10))
449 verticalCenter: parent.verticalCenter
453 name: "ubuntu-store-symbolic"
455 anchors.horizontalCenter: parent.horizontalCenter
461 anchors.horizontalCenter: parent.horizontalCenter
462 anchors.top: storeImage.bottom
463 text: i18n.tr("Store")
467 // Just zoom from the middle
468 scopesOverviewXYScaler.restorePosition = undefined;
469 scopesOverviewXYScaler.restoreSize = allCardSize;
470 scope.performQuery("scope://com.canonical.scopes.clickstore");
478 objectName: "scopesOverviewPreviewListView"
480 scopeStyle: overviewScopeStyle
481 showSignatureLine: false
484 height: parent.height
485 anchors.left: scopesOverviewContent.right
487 onBackClicked: open = false
491 id: scopesOverviewXYScaler
493 height: parent.height
498 property bool animationsEnabled: root.showingNonFavoriteScope || root.growingDashFromPos
500 property var restorePosition
501 property var restoreSize
504 enabled: scopesOverviewXYScaler.animationsEnabled
505 UbuntuNumberAnimation { }
508 enabled: scopesOverviewXYScaler.animationsEnabled
509 UbuntuNumberAnimation { }
511 Behavior on opacity {
512 enabled: scopesOverviewXYScaler.animationsEnabled
513 UbuntuNumberAnimation { }
516 enabled: scopesOverviewXYScaler.animationsEnabled
517 UbuntuNumberAnimation {
520 if (root.showingNonFavoriteScope && scopesOverviewXYScaler.scale != 1) {
521 root.scope.closeScope(tempScopeItem.scope);
522 tempScopeItem.scope = null;
523 } else if (root.growingDashFromPos) {
524 root.growingDashFromPos = false;
532 anchors.fill: tempScopeItem
533 visible: tempScopeItem.visible
534 parent: tempScopeItem.parent
539 objectName: "scopesOverviewTempScopeItem"
542 height: parent.height
544 visible: scope != null
548 var v = scopesOverviewXYScaler.restoreSize.width / tempScopeItem.width;
549 scopesOverviewXYScaler.scale = v;
550 if (scopesOverviewXYScaler.restorePosition) {
551 scopesOverviewXYScaler.x = scopesOverviewXYScaler.restorePosition.x -(tempScopeItem.width - tempScopeItem.width * v) / 2;
552 scopesOverviewXYScaler.y = scopesOverviewXYScaler.restorePosition.y -(tempScopeItem.height - tempScopeItem.height * v) / 2;
554 scopesOverviewXYScaler.x = 0;
555 scopesOverviewXYScaler.y = 0;
557 scopesOverviewXYScaler.opacity = 0;
558 middleItems.overrideOpacity = -1;
560 // TODO Add tests for these connections
562 target: tempScopeItem.scope
564 // TODO Animate the newly opened scope into the foreground (stacked on top of the current scope)
565 tempScopeItem.scope = scope;
568 tempScopeItem.backClicked();
570 root.scope.gotoScope(scopeId);