2 * Copyright (C) 2013-2015 Canonical, Ltd.
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.
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.
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/>.
18 import "../Components"
19 import Ubuntu.Components 1.3
20 import Ubuntu.Gestures 0.1
21 import Unity.Launcher 0.1
22 import Utils 0.1 as Utils
27 readonly property int ignoreHideIfMouseOverLauncher: 1
29 property bool autohideEnabled: false
30 property bool lockedVisible: false
31 property bool available: true // can be used to disable all interactions
32 property alias inverted: panel.inverted
33 property Item blurSource: null
34 property int topPanelHeight: 0
35 property bool drawerEnabled: true
37 property int panelWidth: units.gu(10)
38 property int dragAreaWidth: units.gu(1)
39 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
40 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
42 property bool superPressed: false
43 property bool superTabPressed: false
45 readonly property bool dragging: dragArea.dragging
46 readonly property real dragDistance: dragArea.dragging ? dragArea.touchPosition.x : 0
47 readonly property real visibleWidth: panel.width + panel.x
48 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
50 readonly property bool shown: panel.x > -panel.width
51 readonly property bool drawerShown: drawer.x == 0
53 // emitted when an application is selected
54 signal launcherApplicationSelected(string appId)
56 // emitted when the dash icon in the launcher has been tapped
61 panel.dismissTimer.stop()
63 panel.dismissTimer.restart()
67 onSuperPressedChanged: {
68 if (state == "drawer")
72 superPressTimer.start();
73 superLongPressTimer.start();
75 superPressTimer.stop();
76 superLongPressTimer.stop();
77 launcher.switchToNextState("");
78 panel.shortcutHintsShown = false;
82 onSuperTabPressedChanged: {
83 if (superTabPressed) {
84 switchToNextState("visible")
85 panel.highlightIndex = -1;
87 superPressTimer.stop();
88 superLongPressTimer.stop();
90 if (panel.highlightIndex == -1) {
92 } else if (panel.highlightIndex >= 0){
93 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
95 panel.highlightIndex = -2;
96 switchToNextState("");
101 onLockedVisibleChanged: {
102 if (lockedVisible && state == "") {
103 panel.dismissTimer.stop();
104 fadeOutAnimation.stop();
105 switchToNextState("visible")
106 } else if (!lockedVisible && state == "visible") {
111 function hide(flags) {
112 if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
115 switchToNextState("")
119 if (!root.lockedVisible) {
120 fadeOutAnimation.start();
124 function switchToNextState(state) {
125 animateTimer.nextState = state
126 animateTimer.start();
130 if (available && !dragArea.dragging) {
131 teaseTimer.mode = "teasing"
137 if (available && root.state == "") {
138 teaseTimer.mode = "hinting"
143 function pushEdge(amount) {
144 if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
145 edgeBarrier.push(amount);
149 function openForKeyboardNavigation() {
150 panel.highlightIndex = -1; // The BFB
152 switchToNextState("visible")
155 function openDrawer(focusInputField) {
156 if (!drawerEnabled) {
160 panel.shortcutHintsShown = false;
161 superPressTimer.stop();
162 superLongPressTimer.stop();
165 if (focusInputField) {
168 switchToNextState("drawer")
174 panel.highlightPrevious();
175 event.accepted = true;
179 panel.highlightNext()
181 panel.highlightPrevious();
183 event.accepted = true;
186 panel.highlightNext();
187 event.accepted = true;
191 panel.highlightPrevious();
193 panel.highlightNext();
195 event.accepted = true;
199 panel.openQuicklist(panel.highlightIndex)
200 event.accepted = true;
203 panel.highlightIndex = -2;
204 // Falling through intentionally
208 if (panel.highlightIndex == -1) {
210 } else if (panel.highlightIndex >= 0) {
211 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
214 panel.highlightIndex = -2
215 event.accepted = true;
224 switchToNextState("visible")
229 id: superLongPressTimer
232 switchToNextState("visible")
233 panel.shortcutHintsShown = true;
239 interval: mode == "teasing" ? 200 : 300
240 property string mode: "teasing"
243 // Because the animation on x is disabled while dragging
244 // switching state directly in the drag handlers would not animate
245 // the completion of the hide/reveal gesture. Lets update the state
246 // machine and switch to the final state in the next event loop run
249 objectName: "animateTimer"
251 property string nextState: ""
253 if (root.lockedVisible && nextState == "") {
254 // Due to binding updates when switching between modes
255 // it could happen that our request to show will be overwritten
256 // with a hide request. Rewrite it when we know hiding is not allowed.
257 nextState = "visible"
260 // switching to an intermediate state here to make sure all the
261 // values are restored, even if we were already in the target state
263 root.state = nextState
268 target: LauncherModel
274 onLanguageChanged: LauncherModel.refresh()
277 SequentialAnimation {
281 animateTimer.stop(); // Don't change the state behind our back
282 panel.layer.enabled = true
285 UbuntuNumberAnimation {
288 easing.type: Easing.InQuad
293 panel.layer.enabled = false
294 panel.animate = false;
296 panel.x = -panel.width
298 panel.animate = true;
306 enabled: root.state == "visible" || root.state == "drawer"
309 mouse.accepted = false;
310 panel.highlightIndex = -2;
317 enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary") && !root.lockedVisible
319 anchors.rightMargin: -units.gu(2)
327 if (panel.x < -panel.width/3) {
328 root.switchToNextState("")
330 root.switchToNextState("visible")
338 anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
339 visible: root.blurSource && drawer.x > -drawer.width
340 blurAmount: units.gu(6)
341 sourceItem: root.blurSource
342 blurRect: Qt.rect(panel.width,
344 drawer.width + drawer.x - panel.width,
345 height - root.topPanelHeight)
346 cached: drawer.moving
354 topMargin: root.inverted ? root.topPanelHeight : 0
355 bottom: parent.bottom
358 width: Math.min(root.width, units.gu(90)) * .9
359 panelWidth: panel.width
362 Behavior on anchors.rightMargin {
363 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
366 easing.type: Easing.OutCubic
370 onApplicationSelected: {
372 root.launcherApplicationSelected(appId)
376 Keys.onEscapePressed: {
377 switchToNextState("");
381 onDragDistanceChanged: {
382 anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
384 onDraggingHorizontallyChanged: {
385 if (!draggingHorizontally) {
386 if (drawer.x < -units.gu(10)) {
397 objectName: "launcherPanel"
398 enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
399 width: root.panelWidth
402 bottom: parent.bottom
405 visible: root.x > 0 || x > -width || dragArea.pressed
408 property var dismissTimer: Timer { interval: 500 }
410 target: panel.dismissTimer
412 if (root.autohideEnabled && !root.lockedVisible) {
413 if (!panel.preventHiding) {
416 panel.dismissTimer.restart()
422 property bool animate: true
424 onApplicationSelected: {
425 root.hide(ignoreHideIfMouseOverLauncher);
426 launcherApplicationSelected(appId)
429 root.hide(ignoreHideIfMouseOverLauncher);
433 onPreventHidingChanged: {
434 if (panel.dismissTimer.running) {
435 panel.dismissTimer.restart();
439 onKbdNavigationCancelled: {
440 panel.highlightIndex = -2;
446 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
449 easing.type: Easing.OutCubic
453 Behavior on opacity {
455 duration: UbuntuAnimation.FastDuration; easing.type: Easing.OutCubic
464 enabled: root.available
466 if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
467 root.switchToNextState("visibleTemporary");
471 if (root.drawerEnabled) {
472 root.switchToNextState("drawer");
476 material: Component {
482 anchors.centerIn: parent
484 GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
485 GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
494 objectName: "launcherDragArea"
496 direction: Direction.Rightwards
498 enabled: root.available
499 x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
500 width: root.dragAreaWidth
503 function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
505 property var lastDragPoints: []
507 function dragDirection() {
508 if (lastDragPoints.length < 5) {
514 for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
515 if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
518 if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
522 return toRight ? "right" : toLeft ? "left" : "unknown";
526 if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
527 panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
530 if (root.drawerEnabled && dragging && launcher.state != "drawer") {
531 lastDragPoints.push(distance)
532 var drawerHintDistance = panel.width + units.gu(1)
533 if (distance < drawerHintDistance) {
534 drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
536 var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
537 var linearDrawerProgress = linearDrawerX / (drawer.width)
538 var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
539 drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
546 if (distance > panel.width / 2) {
547 if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
548 root.switchToNextState("drawer");
551 root.switchToNextState("visible");
553 } else if (root.state === "") {
554 // didn't drag far enough. rollback
555 root.switchToNextState("");
564 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
571 anchors.rightMargin: 0
578 x: -root.x // so we never go past panelWidth, even when teased by tutorial
582 anchors.rightMargin: 0
590 anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
594 name: "visibleTemporary"
598 autohideEnabled: true
603 when: teaseTimer.running && teaseTimer.mode == "teasing"
606 x: -root.panelWidth + units.gu(2)
611 when: teaseTimer.running && teaseTimer.mode == "hinting"