Unity 8
Dash.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.2
18 import Ubuntu.Components 0.1
19 import Ubuntu.Gestures 0.1
20 import Unity 0.2
21 import Utils 0.1
22 import Unity.DashCommunicator 0.1
23 import "../Components"
24 
25 Showable {
26  id: dash
27  objectName: "dash"
28 
29  visible: shown
30 
31  Connections {
32  target: UriHandler
33  onOpened: {
34  backToDashContent()
35  dashContent.currentScope.performQuery(uris[0])
36  }
37  }
38 
39  DashCommunicatorService {
40  objectName: "dashCommunicatorService"
41  onSetCurrentScopeRequested: {
42  if (!isSwipe || !window.active || bottomEdgeController.progress != 0 || scopeItem.scope || dashContent.subPageShown) {
43  if (bottomEdgeController.progress != 0 && window.active) animate = false;
44  dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
45  backToDashContent()
46  }
47  }
48  }
49 
50  function backToDashContent()
51  {
52  // Close dash overview and nested temp scopes in it
53  if (bottomEdgeController.progress != 0) {
54  bottomEdgeController.enableAnimation = window.active;
55  bottomEdgeController.progress = 0;
56  }
57  // Close normal temp scopes (e.g. App Store)
58  if (scopeItem.scope) {
59  scopeItem.backClicked();
60  }
61  // Close previews
62  if (dashContent.subPageShown) {
63  dashContent.closePreview();
64  }
65  }
66 
67  function setCurrentScope(scopeId, animate, reset) {
68  var scopeIndex = -1;
69  for (var i = 0; i < scopes.count; ++i) {
70  if (scopes.getScope(i).id == scopeId) {
71  scopeIndex = i;
72  break;
73  }
74  }
75 
76  if (scopeIndex == -1) {
77  console.warn("No match for scope with id: %1".arg(scopeId))
78  return
79  }
80 
81  closeOverlayScope();
82 
83  dashContent.closePreview();
84 
85  if (scopeIndex == dashContent.currentIndex && !reset) {
86  // the scope is already the current one
87  return
88  }
89 
90  dashContent.workaroundRestoreIndex = -1;
91  dashContent.setCurrentScopeAtIndex(scopeIndex, animate, reset)
92  }
93 
94  function closeOverlayScope() {
95  if (dashContent.x != 0) {
96  dashContent.x = 0;
97  }
98  }
99 
100  Scopes {
101  id: scopes
102  }
103 
104  QtObject {
105  id: bottomEdgeController
106  objectName: "bottomEdgeController"
107 
108  property alias enableAnimation: progressAnimation.enabled
109  property real progress: 0
110  Behavior on progress {
111  id: progressAnimation
112  UbuntuNumberAnimation { }
113  }
114 
115  onProgressChanged: {
116  // FIXME This is to workaround a Qt bug with the model moving the current item
117  // when the list is ListView.SnapOneItem and ListView.StrictlyEnforceRange
118  // together with the code in DashContent.qml
119  if (dashContent.workaroundRestoreIndex != -1) {
120  dashContent.currentIndex = dashContent.workaroundRestoreIndex;
121  dashContent.workaroundRestoreIndex = -1;
122  }
123  }
124  }
125 
126  DashContent {
127  id: dashContent
128 
129  objectName: "dashContent"
130  width: dash.width
131  height: dash.height
132  scopes: scopes
133  visible: x != -width
134  onGotoScope: {
135  dash.setCurrentScope(scopeId, true, false);
136  }
137  onOpenScope: {
138  scopeItem.scope = scope;
139  x = -width;
140  }
141  Behavior on x {
142  UbuntuNumberAnimation {
143  onRunningChanged: {
144  if (!running && dashContent.x == 0) {
145  scopes.closeScope(scopeItem.scope);
146  scopeItem.scope = null;
147  }
148  }
149  }
150  }
151 
152  // This is to avoid the situation where a bottom-edge swipe would bring up the dash overview
153  // (as expected) but would also cause the dash content flickable to move a bit, because
154  // that flickable was getting the touch events while overviewDragHandle was still undecided
155  // about whether that touch was indeed performing a directional drag gesture.
156  forceNonInteractive: overviewDragHandle.status != DirectionalDragArea.WaitingForTouch
157 
158  enabled: bottomEdgeController.progress == 0
159  }
160 
161  Rectangle {
162  color: "black"
163  opacity: bottomEdgeController.progress
164  anchors.fill: dashContent
165  }
166 
167  ScopesList {
168  id: scopesList
169  objectName: "scopesList"
170  width: dash.width
171  height: dash.height
172  scope: scopes.overviewScope
173  y: dash.height * (1 - bottomEdgeController.progress)
174  visible: bottomEdgeController.progress != 0
175  onBackClicked: {
176  bottomEdgeController.enableAnimation = true;
177  bottomEdgeController.progress = 0;
178  }
179  onStoreClicked: {
180  bottomEdgeController.enableAnimation = true;
181  bottomEdgeController.progress = 0;
182  scopesList.scope.performQuery("scope://com.canonical.scopes.clickstore");
183  }
184  onRequestFavorite: {
185  scopes.setFavorite(scopeId, favorite);
186  }
187  onRequestFavoriteMoveTo: {
188  scopes.moveFavoriteTo(scopeId, index);
189  }
190 
191  Binding {
192  target: scopesList.scope
193  property: "isActive"
194  value: bottomEdgeController.progress === 1 && (Qt.application.state == Qt.ApplicationActive)
195  }
196 
197  Connections {
198  target: scopesList.scope
199  onOpenScope: {
200  bottomEdgeController.enableAnimation = true;
201  bottomEdgeController.progress = 0;
202  scopeItem.scope = scope;
203  dashContent.x = -dashContent.width;
204  }
205  onGotoScope: {
206  bottomEdgeController.enableAnimation = true;
207  bottomEdgeController.progress = 0;
208  dashContent.gotoScope(scopeId);
209  }
210  }
211  }
212 
213  DashBackground {
214  anchors.fill: scopeItem
215  visible: scopeItem.visible
216  }
217 
218  GenericScopeView {
219  id: scopeItem
220  objectName: "dashTempScopeItem"
221 
222  x: dashContent.x + width
223  y: dashContent.y
224  width: parent.width
225  height: parent.height
226  visible: scope != null
227  hasBackAction: true
228  isCurrent: visible
229  onBackClicked: {
230  closeOverlayScope();
231  closePreview();
232  }
233 
234  Connections {
235  target: scopeItem.scope
236  onGotoScope: {
237  dashContent.gotoScope(scopeId);
238  }
239  onOpenScope: {
240  scopeItem.closePreview();
241  var oldScope = scopeItem.scope;
242  scopeItem.scope = scope;
243  scopes.closeScope(oldScope);
244  }
245  }
246  }
247 
248  Rectangle {
249  id: indicator
250  objectName: "processingIndicator"
251  anchors {
252  left: parent.left
253  right: parent.right
254  bottom: parent.bottom
255  bottomMargin: Qt.inputMethod.keyboardRectangle.height
256  }
257  height: units.dp(3)
258  color: scopeStyle.backgroundLuminance > 0.7 ? "#50000000" : "#50ffffff"
259  opacity: 0
260  visible: opacity > 0
261 
262  readonly property bool processing: dashContent.processing || scopeItem.processing || scopesList.processing
263 
264  Behavior on opacity {
265  UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
266  }
267 
268  onProcessingChanged: {
269  if (processing) delay.start();
270  else if (!persist.running) indicator.opacity = 0;
271  }
272 
273  Timer {
274  id: delay
275  interval: 200
276  onTriggered: if (indicator.processing) {
277  persist.restart();
278  indicator.opacity = 1;
279  }
280  }
281 
282  Timer {
283  id: persist
284  interval: 2 * UbuntuAnimation.SleepyDuration - UbuntuAnimation.FastDuration
285  onTriggered: if (!indicator.processing) indicator.opacity = 0
286  }
287 
288  Rectangle {
289  id: orange
290  anchors { top: parent.top; bottom: parent.bottom }
291  width: parent.width / 4
292  color: UbuntuColors.orange
293 
294  SequentialAnimation {
295  running: indicator.visible
296  loops: Animation.Infinite
297  XAnimator {
298  from: -orange.width / 2
299  to: indicator.width - orange.width / 2
300  duration: UbuntuAnimation.SleepyDuration
301  easing.type: Easing.InOutSine
302  target: orange
303  }
304  XAnimator {
305  from: indicator.width - orange.width / 2
306  to: -orange.width / 2
307  duration: UbuntuAnimation.SleepyDuration
308  easing.type: Easing.InOutSine
309  target: orange
310  }
311  }
312  }
313  }
314 
315  Image {
316  objectName: "overviewHint"
317  source: "graphics/overview_hint.png"
318  anchors.horizontalCenter: parent.horizontalCenter
319  opacity: !scopeItem.scope && (scopes.count == 0 || dashContent.pageHeaderTotallyVisible) &&
320  (overviewDragHandle.enabled || bottomEdgeController.progress != 0) ? 1 : 0
321  Behavior on opacity {
322  enabled: bottomEdgeController.progress == 0
323  UbuntuNumberAnimation {}
324  }
325  y: parent.height - height * (1 - bottomEdgeController.progress * 4)
326  MouseArea {
327  // Eat direct presses on the overview hint so that they do not end up in the card below
328  anchors.fill: parent
329  enabled: parent.opacity != 0
330  }
331  }
332 
333  EdgeDragArea {
334  id: overviewDragHandle
335  objectName: "overviewDragHandle"
336  z: 1
337  direction: Direction.Upwards
338  enabled: !dashContent.subPageShown &&
339  (scopes.count == 0 || (dashContent.currentScope && dashContent.currentScope.searchQuery == "")) &&
340  !scopeItem.scope &&
341  (bottomEdgeController.progress == 0 || dragging)
342 
343  readonly property real fullMovement: dash.height
344 
345  anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
346  height: units.gu(2)
347 
348  onSceneDistanceChanged: {
349  if (status == DirectionalDragArea.Recognized) {
350  bottomEdgeController.enableAnimation = false;
351  bottomEdgeController.progress = Math.max(0, Math.min(1, sceneDistance / fullMovement));
352  }
353  }
354 
355  property int previousStatus: -1
356  property int currentStatus: DirectionalDragArea.WaitingForTouch
357 
358  onStatusChanged: {
359  previousStatus = currentStatus;
360  currentStatus = status;
361 
362  if (status == DirectionalDragArea.WaitingForTouch &&
363  previousStatus == DirectionalDragArea.Recognized) {
364  bottomEdgeController.enableAnimation = true;
365  bottomEdgeController.progress = (bottomEdgeController.progress > 0.2) ? 1 : 0;
366  }
367  }
368  }
369 }