on 6ca9421 help sign in
unity8/qml/Launcher/Launcher.qml
352 lines 10 kB history raw
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 */
17import QtQuick 2.0
18import "../Components"
19import Ubuntu.Components 0.1
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
23Item {
24 id: root
26 property bool autohideEnabled: false
27 property bool available: true // can be used to disable all interactions
28 property alias inverted: panel.inverted
29 property bool shadeBackground: true // can be used to disable background shade when launcher is visible
31 property int panelWidth: units.gu(8)
32 property int dragAreaWidth: units.gu(1)
33 property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
35 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
37 readonly property bool dragging: dragArea.dragging
38 readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
39 readonly property real visibleWidth: panel.width + panel.x
41 readonly property bool shown: panel.x > -panel.width
43 // emitted when an application is selected
44 signal launcherApplicationSelected(string appId)
46 // emitted when the apps dash should be shown because of a swipe gesture
47 signal dash()
49 // emitted when the dash icon in the launcher has been tapped
50 signal showDashHome()
52 onStateChanged: {
53 if (state == "") {
54 dismissTimer.stop()
55 } else {
56 dismissTimer.restart()
57 }
58 }
60 function hide() {
61 switchToNextState("")
62 }
64 function fadeOut() {
65 fadeOutAnimation.start();
66 }
68 function switchToNextState(state) {
69 animateTimer.nextState = state
70 animateTimer.start();
71 }
73 function tease() {
74 if (available && !dragArea.dragging) {
75 teaseTimer.mode = "teasing"
76 teaseTimer.start();
77 }
78 }
80 function hint() {
81 if (available && root.state == "") {
82 teaseTimer.mode = "hinting"
83 teaseTimer.start();
84 }
85 }
87 Timer {
88 id: teaseTimer
89 interval: mode == "teasing" ? 200 : 300
90 property string mode: "teasing"
91 }
93 Timer {
94 id: dismissTimer
95 objectName: "dismissTimer"
96 interval: 500
97 onTriggered: {
98 if (root.autohideEnabled) {
99 if (!panel.preventHiding && !hoverArea.containsMouse) {
100 root.state = ""
101 } else {
102 dismissTimer.restart()
103 }
104 }
105 }
106 }
108 // Because the animation on x is disabled while dragging
109 // switching state directly in the drag handlers would not animate
110 // the completion of the hide/reveal gesture. Lets update the state
111 // machine and switch to the final state in the next event loop run
112 Timer {
113 id: animateTimer
114 objectName: "animateTimer"
115 interval: 1
116 property string nextState: ""
117 onTriggered: {
118 // switching to an intermediate state here to make sure all the
119 // values are restored, even if we were already in the target state
120 root.state = "tmp"
121 root.state = nextState
122 }
123 }
125 Connections {
126 target: LauncherModel
127 onHint: hint();
128 }
130 Connections {
131 target: i18n
132 onLanguageChanged: LauncherModel.refresh()
133 }
135 SequentialAnimation {
136 id: fadeOutAnimation
137 ScriptAction {
138 script: {
139 animateTimer.stop(); // Don't change the state behind our back
140 panel.layer.enabled = true
141 }
142 }
143 UbuntuNumberAnimation {
144 target: panel
145 property: "opacity"
146 easing.type: Easing.InQuad
147 to: 0
148 }
149 ScriptAction {
150 script: {
151 panel.layer.enabled = false
152 panel.animate = false;
153 root.state = "";
154 panel.x = -panel.width
155 panel.opacity = 1;
156 panel.animate = true;
157 }
158 }
159 }
161 MouseArea {
162 id: launcherDragArea
163 enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary")
164 anchors.fill: panel
165 anchors.rightMargin: -units.gu(2)
166 drag {
167 axis: Drag.XAxis
168 maximumX: 0
169 target: panel
170 }
172 onReleased: {
173 if (panel.x < -panel.width/3) {
174 root.switchToNextState("")
175 } else {
176 root.switchToNextState("visible")
177 }
178 }
180 }
182 MultiPointTouchArea {
183 id: closeMouseArea
184 anchors {
185 left: launcherDragArea.right
186 top: parent.top
187 right: parent.right
188 bottom: parent.bottom
189 }
190 enabled: root.shadeBackground && root.state == "visible"
191 onPressed: {
192 root.state = ""
193 }
194 }
196 Rectangle {
197 id: backgroundShade
198 anchors.fill: parent
199 color: "black"
200 opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
202 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
203 }
205 LauncherPanel {
206 id: panel
207 objectName: "launcherPanel"
208 enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
209 width: root.panelWidth
210 anchors {
211 top: parent.top
212 bottom: parent.bottom
213 }
214 x: -width
215 visible: root.x > 0 || x > -width || dragArea.pressed
216 model: LauncherModel
218 property bool animate: true
220 onApplicationSelected: {
221 root.state = ""
222 launcherApplicationSelected(appId)
223 }
224 onShowDashHome: {
225 root.state = ""
226 root.showDashHome();
227 }
229 onPreventHidingChanged: {
230 if (dismissTimer.running) {
231 dismissTimer.restart();
232 }
233 }
235 Behavior on x {
236 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
237 NumberAnimation {
238 duration: 300
239 easing.type: Easing.OutCubic
240 }
241 }
243 Behavior on opacity {
244 NumberAnimation {
245 duration: UbuntuAnimation.FastDuration; easing.type: Easing.OutCubic
246 }
247 }
248 }
250 // TODO: This should be replaced by some mechanism that reveals the launcher
251 // after a certain resistance has been overcome, like unity7 does. However,
252 // as we don't get relative mouse coordinates yet, this will do for now.
253 MouseArea {
254 id: hoverArea
255 anchors { fill: panel; rightMargin: -1 }
256 hoverEnabled: true
257 propagateComposedEvents: true
258 onContainsMouseChanged: {
259 if (containsMouse) {
260 root.switchToNextState("visibleTemporary");
261 } else {
262 dismissTimer.restart();
263 }
264 }
265 onPressed: mouse.accepted = false;
267 // We need to eat touch events here in order to make sure that
268 // we don't trigger both, the dragArea and the hoverArea
269 MultiPointTouchArea {
270 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
271 width: units.dp(1)
272 mouseEnabled: false
273 enabled: parent.enabled
274 }
275 }
277 DirectionalDragArea {
278 id: dragArea
279 objectName: "launcherDragArea"
281 direction: Direction.Rightwards
283 enabled: root.available
284 x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
285 width: root.dragAreaWidth
286 height: root.height
288 onDistanceChanged: {
289 if (!dragging || launcher.state == "visible")
290 return;
292 panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
293 }
295 onDraggingChanged: {
296 if (!dragging) {
297 if (distance > panel.width / 2) {
298 root.switchToNextState("visible")
299 if (distance > minimizeDistance) {
300 root.dash();
301 }
302 } else if (root.state === "") {
303 // didn't drag far enough. rollback
304 root.switchToNextState("")
305 }
306 }
307 }
308 }
310 states: [
311 State {
312 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
313 PropertyChanges {
314 target: panel
315 x: -root.panelWidth
316 }
317 },
318 State {
319 name: "visible"
320 PropertyChanges {
321 target: panel
322 x: -root.x // so we never go past panelWidth, even when teased by tutorial
323 }
324 PropertyChanges { target: hoverArea; enabled: false }
325 },
326 State {
327 name: "visibleTemporary"
328 extend: "visible"
329 PropertyChanges {
330 target: root
331 autohideEnabled: true
332 }
333 PropertyChanges { target: hoverArea; enabled: true }
334 },
335 State {
336 name: "teasing"
337 when: teaseTimer.running && teaseTimer.mode == "teasing"
338 PropertyChanges {
339 target: panel
340 x: -root.panelWidth + units.gu(2)
341 }
342 },
343 State {
344 name: "hinting"
345 when: teaseTimer.running && teaseTimer.mode == "hinting"
346 PropertyChanges {
347 target: panel
348 x: 0
349 }
350 }
351 ]