on xenial_-_blue-light-filter help sign in
unity8/qml/Launcher/Launcher.qml
470 lines 14 kB history raw
1/*
2 * Copyright (C) 2013-2015 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.4
18import "../Components"
19import Ubuntu.Components 1.3
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
23FocusScope {
24 id: root
26 property bool autohideEnabled: false
27 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
31 property int panelWidth: units.gu(10)
32 property int dragAreaWidth: units.gu(1)
33 property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
35 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
37 property bool superPressed: false
38 property bool superTabPressed: false
40 readonly property bool dragging: dragArea.dragging
41 readonly property real dragDistance: dragArea.dragging ? dragArea.touchPosition.x : 0
42 readonly property real visibleWidth: panel.width + panel.x
43 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
45 readonly property bool shown: panel.x > -panel.width
47 // emitted when an application is selected
48 signal launcherApplicationSelected(string appId)
50 // emitted when the apps dash should be shown because of a swipe gesture
51 signal dash()
53 // emitted when the dash icon in the launcher has been tapped
54 signal showDashHome()
56 onStateChanged: {
57 if (state == "") {
58 panel.dismissTimer.stop()
59 } else {
60 panel.dismissTimer.restart()
61 }
62 }
64 onSuperPressedChanged: {
65 if (superPressed) {
66 superPressTimer.start();
67 superLongPressTimer.start();
68 } else {
69 superPressTimer.stop();
70 superLongPressTimer.stop();
71 launcher.switchToNextState("");
72 panel.shortcutHintsShown = false;
73 }
74 }
76 onSuperTabPressedChanged: {
77 if (superTabPressed) {
78 switchToNextState("visible")
79 panel.highlightIndex = -1;
80 root.focus = true;
81 superPressTimer.stop();
82 superLongPressTimer.stop();
83 } else {
84 if (panel.highlightIndex == -1) {
85 showDashHome();
86 } else if (panel.highlightIndex >= 0){
87 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
88 }
89 panel.highlightIndex = -2;
90 switchToNextState("");
91 root.focus = false;
92 }
93 }
95 onLockedVisibleChanged: {
96 if (lockedVisible && state == "") {
97 panel.dismissTimer.stop();
98 fadeOutAnimation.stop();
99 switchToNextState("visible")
100 } else if (!lockedVisible && state == "visible") {
101 hide();
102 }
103 }
105 function hide() {
106 switchToNextState("")
107 }
109 function fadeOut() {
110 if (!root.lockedVisible) {
111 fadeOutAnimation.start();
112 }
113 }
115 function switchToNextState(state) {
116 animateTimer.nextState = state
117 animateTimer.start();
118 }
120 function tease() {
121 if (available && !dragArea.dragging) {
122 teaseTimer.mode = "teasing"
123 teaseTimer.start();
124 }
125 }
127 function hint() {
128 if (available && root.state == "") {
129 teaseTimer.mode = "hinting"
130 teaseTimer.start();
131 }
132 }
134 function pushEdge(amount) {
135 if (root.state === "") {
136 edgeBarrier.push(amount);
137 }
138 }
140 function openForKeyboardNavigation() {
141 panel.highlightIndex = -1; // The BFB
142 root.focus = true;
143 switchToNextState("visible")
144 }
146 Keys.onPressed: {
147 switch (event.key) {
148 case Qt.Key_Backtab:
149 panel.highlightPrevious();
150 event.accepted = true;
151 break;
152 case Qt.Key_Up:
153 if (root.inverted) {
154 panel.highlightNext()
155 } else {
156 panel.highlightPrevious();
157 }
158 event.accepted = true;
159 break;
160 case Qt.Key_Tab:
161 panel.highlightNext();
162 event.accepted = true;
163 break;
164 case Qt.Key_Down:
165 if (root.inverted) {
166 panel.highlightPrevious();
167 } else {
168 panel.highlightNext();
169 }
170 event.accepted = true;
171 break;
172 case Qt.Key_Right:
173 case Qt.Key_Menu:
174 panel.openQuicklist(panel.highlightIndex)
175 event.accepted = true;
176 break;
177 case Qt.Key_Escape:
178 panel.highlightIndex = -2;
179 // Falling through intentionally
180 case Qt.Key_Enter:
181 case Qt.Key_Return:
182 case Qt.Key_Space:
183 if (panel.highlightIndex == -1) {
184 showDashHome();
185 } else if (panel.highlightIndex >= 0) {
186 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
187 }
188 root.hide();
189 panel.highlightIndex = -2
190 event.accepted = true;
191 root.focus = false;
192 }
193 }
195 Timer {
196 id: superPressTimer
197 interval: 200
198 onTriggered: {
199 switchToNextState("visible")
200 }
201 }
203 Timer {
204 id: superLongPressTimer
205 interval: 1000
206 onTriggered: {
207 switchToNextState("visible")
208 panel.shortcutHintsShown = true;
209 }
210 }
212 Timer {
213 id: teaseTimer
214 interval: mode == "teasing" ? 200 : 300
215 property string mode: "teasing"
216 }
218 // Because the animation on x is disabled while dragging
219 // switching state directly in the drag handlers would not animate
220 // the completion of the hide/reveal gesture. Lets update the state
221 // machine and switch to the final state in the next event loop run
222 Timer {
223 id: animateTimer
224 objectName: "animateTimer"
225 interval: 1
226 property string nextState: ""
227 onTriggered: {
228 if (root.lockedVisible && nextState == "") {
229 // Due to binding updates when switching between modes
230 // it could happen that our request to show will be overwritten
231 // with a hide request. Rewrite it when we know hiding is not allowed.
232 nextState = "visible"
233 }
235 // switching to an intermediate state here to make sure all the
236 // values are restored, even if we were already in the target state
237 root.state = "tmp"
238 root.state = nextState
239 }
240 }
242 Connections {
243 target: LauncherModel
244 onHint: hint();
245 }
247 Connections {
248 target: i18n
249 onLanguageChanged: LauncherModel.refresh()
250 }
252 SequentialAnimation {
253 id: fadeOutAnimation
254 ScriptAction {
255 script: {
256 animateTimer.stop(); // Don't change the state behind our back
257 panel.layer.enabled = true
258 }
259 }
260 UbuntuNumberAnimation {
261 target: panel
262 property: "opacity"
263 easing.type: Easing.InQuad
264 to: 0
265 }
266 ScriptAction {
267 script: {
268 panel.layer.enabled = false
269 panel.animate = false;
270 root.state = "";
271 panel.x = -panel.width
272 panel.opacity = 1;
273 panel.animate = true;
274 }
275 }
276 }
278 InverseMouseArea {
279 id: closeMouseArea
280 anchors.fill: panel
281 enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
282 visible: enabled
283 onPressed: {
284 mouse.accepted = false;
285 panel.highlightIndex = -2;
286 root.hide();
287 }
288 }
290 MouseArea {
291 id: launcherDragArea
292 enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary") && !root.lockedVisible
293 anchors.fill: panel
294 anchors.rightMargin: -units.gu(2)
295 drag {
296 axis: Drag.XAxis
297 maximumX: 0
298 target: panel
299 }
301 onReleased: {
302 if (panel.x < -panel.width/3) {
303 root.switchToNextState("")
304 } else {
305 root.switchToNextState("visible")
306 }
307 }
308 }
310 EdgeBarrier {
311 id: edgeBarrier
312 edge: Qt.LeftEdge
313 target: parent
314 enabled: root.available
315 onPassed: { root.switchToNextState("visibleTemporary"); }
316 material: Component {
317 Item {
318 Rectangle {
319 width: parent.height
320 height: parent.width
321 rotation: -90
322 anchors.centerIn: parent
323 gradient: Gradient {
324 GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
325 GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
326 }
327 }
328 }
329 }
330 }
332 LauncherPanel {
333 id: panel
334 objectName: "launcherPanel"
335 enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
336 width: root.panelWidth
337 anchors {
338 top: parent.top
339 bottom: parent.bottom
340 }
341 x: -width
342 visible: root.x > 0 || x > -width || dragArea.pressed
343 model: LauncherModel
345 property var dismissTimer: Timer { interval: 500 }
346 Connections {
347 target: panel.dismissTimer
348 onTriggered: {
349 if (root.autohideEnabled && !root.lockedVisible) {
350 if (!panel.preventHiding) {
351 root.state = ""
352 } else {
353 panel.dismissTimer.restart()
354 }
355 }
356 }
357 }
359 property bool animate: true
361 onApplicationSelected: {
362 root.hide();
363 launcherApplicationSelected(appId)
364 }
365 onShowDashHome: {
366 root.hide();
367 root.showDashHome();
368 }
370 onPreventHidingChanged: {
371 if (panel.dismissTimer.running) {
372 panel.dismissTimer.restart();
373 }
374 }
376 onKbdNavigationCancelled: {
377 panel.highlightIndex = -2;
378 root.hide();
379 root.focus = false;
380 }
382 Behavior on x {
383 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
384 NumberAnimation {
385 duration: 300
386 easing.type: Easing.OutCubic
387 }
388 }
390 Behavior on opacity {
391 NumberAnimation {
392 duration: UbuntuAnimation.FastDuration; easing.type: Easing.OutCubic
393 }
394 }
395 }
397 SwipeArea {
398 id: dragArea
399 objectName: "launcherDragArea"
401 direction: Direction.Rightwards
403 enabled: root.available
404 x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
405 width: root.dragAreaWidth
406 height: root.height
408 onDistanceChanged: {
409 if (!dragging || launcher.state == "visible")
410 return;
412 panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
413 }
415 onDraggingChanged: {
416 if (!dragging) {
417 if (distance > panel.width / 2) {
418 root.switchToNextState("visible")
419 if (distance > minimizeDistance) {
420 root.dash();
421 }
422 } else if (root.state === "") {
423 // didn't drag far enough. rollback
424 root.switchToNextState("")
425 }
426 }
427 }
428 }
430 states: [
431 State {
432 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
433 PropertyChanges {
434 target: panel
435 x: -root.panelWidth
436 }
437 },
438 State {
439 name: "visible"
440 PropertyChanges {
441 target: panel
442 x: -root.x // so we never go past panelWidth, even when teased by tutorial
443 }
444 },
445 State {
446 name: "visibleTemporary"
447 extend: "visible"
448 PropertyChanges {
449 target: root
450 autohideEnabled: true
451 }
452 },
453 State {
454 name: "teasing"
455 when: teaseTimer.running && teaseTimer.mode == "teasing"
456 PropertyChanges {
457 target: panel
458 x: -root.panelWidth + units.gu(2)
459 }
460 },
461 State {
462 name: "hinting"
463 when: teaseTimer.running && teaseTimer.mode == "hinting"
464 PropertyChanges {
465 target: panel
466 x: 0
467 }
468 }
469 ]