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