Unity 8
 All Classes Functions Properties
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)) :
33  (dragArea.dragging ? 0.001 : 0)
34 
35  readonly property bool shown: panel.x > -panel.width
36 
37  // emitted when an application is selected
38  signal launcherApplicationSelected(string appId)
39 
40  // emitted when the apps dash should be shown because of a swipe gesture
41  signal dash()
42 
43  // emitted when the dash icon in the launcher has been tapped
44  signal showDashHome()
45 
46  onStateChanged: {
47  if (state == "") {
48  dismissTimer.stop()
49  } else {
50  dismissTimer.restart()
51  }
52  }
53 
54  function hide() {
55  switchToNextState("")
56  }
57 
58  function fadeOut() {
59  fadeOutAnimation.start();
60  }
61 
62  function switchToNextState(state) {
63  animateTimer.nextState = state
64  animateTimer.start();
65  }
66 
67  function tease() {
68  if (available) {
69  teaseTimer.start();
70  }
71  }
72 
73  Timer {
74  id: teaseTimer
75  interval: 200
76  }
77 
78  Timer {
79  id: 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  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  opacity: (x == -width && dragArea.status === DirectionalDragArea.WaitingForTouch) ? 0 : 1
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  if (distance > minimizeDistance) {
248  root.dash()
249  } else {
250  root.switchToNextState("visible")
251  }
252  } else {
253  root.switchToNextState("")
254  }
255  }
256  }
257  }
258 
259  states: [
260  State {
261  name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
262  PropertyChanges {
263  target: panel
264  x: -root.panelWidth
265  }
266  },
267  State {
268  name: "visible"
269  PropertyChanges {
270  target: panel
271  x: 0
272  }
273  },
274  State {
275  name: "teasing"
276  when: teaseTimer.running
277  PropertyChanges {
278  target: panel
279  x: -root.panelWidth + units.gu(2)
280  }
281  }
282  ]
283 }