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