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