Unity 8
 All Classes Functions
Launcher.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.0
18 import "../Components"
19 import Ubuntu.Components 0.1
20 import Ubuntu.Gestures 0.1
21 import Unity.Launcher 0.1
22 
23 Item {
24  id: root
25 
26  property bool available: true // can be used to disable all interactions
27 
28  property int panelWidth: units.gu(8)
29  property int dragAreaWidth: units.gu(1)
30  property int minimizeDistance: units.gu(26)
31  property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
32  (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
33 
34  readonly property bool shown: panel.x > -panel.width
35 
36  // emitted when an application is selected
37  signal launcherApplicationSelected(string appId)
38 
39  // emitted when the apps dash should be shown because of a swipe gesture
40  signal dash()
41 
42  // emitted when the dash icon in the launcher has been tapped
43  signal showDashHome()
44 
45  onStateChanged: {
46  if (state == "") {
47  dismissTimer.stop()
48  } else {
49  dismissTimer.restart()
50  }
51  }
52 
53  function hide() {
54  switchToNextState("")
55  }
56 
57  function fadeOut() {
58  fadeOutAnimation.start();
59  }
60 
61  function switchToNextState(state) {
62  animateTimer.nextState = state
63  animateTimer.start();
64  }
65 
66  function tease() {
67  if (available) {
68  teaseTimer.start();
69  }
70  }
71 
72  Timer {
73  id: teaseTimer
74  interval: 200
75  }
76 
77  Timer {
78  id: dismissTimer
79  objectName: "dismissTimer"
80  interval: 5000
81  onTriggered: {
82  if (!panel.preventHiding) {
83  root.state = ""
84  } else {
85  dismissTimer.restart()
86  }
87  }
88  }
89 
90  // Because the animation on x is disabled while dragging
91  // switching state directly in the drag handlers would not animate
92  // the completion of the hide/reveal gesture. Lets update the state
93  // machine and switch to the final state in the next event loop run
94  Timer {
95  id: animateTimer
96  objectName: "animateTimer"
97  interval: 1
98  property string nextState: ""
99  onTriggered: {
100  // switching to an intermediate state here to make sure all the
101  // values are restored, even if we were already in the target state
102  root.state = "tmp"
103  root.state = nextState
104  }
105  }
106 
107  SequentialAnimation {
108  id: fadeOutAnimation
109  ScriptAction {
110  script: {
111  panel.layer.enabled = true
112  }
113  }
114  UbuntuNumberAnimation {
115  target: panel
116  property: "opacity"
117  easing.type: Easing.InQuad
118  to: 0
119  }
120  ScriptAction {
121  script: {
122  panel.layer.enabled = false
123  panel.animate = false;
124  root.state = "";
125  panel.x = -panel.width
126  panel.opacity = 1;
127  panel.animate = true;
128  }
129  }
130  }
131 
132  MouseArea {
133  id: launcherDragArea
134  enabled: root.state == "visible"
135  anchors.fill: panel
136  anchors.rightMargin: -units.gu(2)
137  drag {
138  axis: Drag.XAxis
139  maximumX: 0
140  target: panel
141  }
142 
143  onReleased: {
144  if (panel.x < -panel.width/3) {
145  root.switchToNextState("")
146  } else {
147  root.switchToNextState("visible")
148  }
149  }
150 
151  }
152  MouseArea {
153  id: closeMouseArea
154  anchors {
155  left: launcherDragArea.right
156  top: parent.top
157  right: parent.right
158  bottom: parent.bottom
159  }
160  enabled: root.state == "visible"
161  onPressed: {
162  root.state = ""
163  }
164  }
165 
166  Rectangle {
167  id: backgroundShade
168  anchors.fill: parent
169  color: "black"
170  opacity: root.state == "visible" ? 0.6 : 0
171 
172  Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
173  }
174 
175  LauncherPanel {
176  id: panel
177  objectName: "launcherPanel"
178  enabled: root.available
179  width: root.panelWidth
180  anchors {
181  top: parent.top
182  bottom: parent.bottom
183  }
184  x: -width
185  visible: x > -width || dragArea.status === DirectionalDragArea.Undecided
186  model: LauncherModel
187 
188  property bool animate: true
189 
190  onApplicationSelected: {
191  root.state = ""
192  launcherApplicationSelected(appId)
193  }
194  onShowDashHome: {
195  root.state = ""
196  root.showDashHome();
197  }
198 
199  onPreventHidingChanged: {
200  if (dismissTimer.running) {
201  dismissTimer.restart();
202  }
203  }
204 
205  Behavior on x {
206  enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
207  NumberAnimation {
208  duration: 300
209  easing.type: Easing.OutCubic
210  }
211  }
212 
213  Behavior on opacity {
214  NumberAnimation {
215  duration: UbuntuAnimation.FastDuration; easing.type: Easing.OutCubic
216  }
217  }
218  }
219 
220  EdgeDragArea {
221  id: dragArea
222  objectName: "launcherDragArea"
223 
224  direction: Direction.Rightwards
225 
226  enabled: root.available
227  width: root.dragAreaWidth
228  height: root.height
229 
230  onTouchXChanged: {
231  if (status !== DirectionalDragArea.Recognized || launcher.state == "visible")
232  return;
233 
234  // When the gesture finally gets recognized, the finger will likely be
235  // reasonably far from the edge. If we made the panel immediately
236  // follow the finger position it would be visually unpleasant as it
237  // would appear right next to the user's finger out of nowhere.
238  // Instead, we make the panel go towards the user's finger in several
239  // steps. ie., in an animated way.
240  var targetPanelX = Math.min(0, touchX - panel.width)
241  var delta = targetPanelX - panel.x
242  // the trick is not to go all the way (1.0) as it would cause a sudden jump
243  panel.x += 0.4 * delta
244  }
245 
246  onDraggingChanged: {
247  if (!dragging) {
248  if (distance > panel.width / 2) {
249  root.switchToNextState("visible")
250  if (distance > minimizeDistance) {
251  root.dash();
252  }
253  } else {
254  root.switchToNextState("")
255  }
256  }
257  }
258  }
259 
260  states: [
261  State {
262  name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
263  PropertyChanges {
264  target: panel
265  x: -root.panelWidth
266  }
267  },
268  State {
269  name: "visible"
270  PropertyChanges {
271  target: panel
272  x: 0
273  }
274  },
275  State {
276  name: "teasing"
277  when: teaseTimer.running
278  PropertyChanges {
279  target: panel
280  x: -root.panelWidth + units.gu(2)
281  }
282  }
283  ]
284 }