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 GlobalShortcut 1.0
27 property bool autohideEnabled: false
28 property bool lockedVisible: false
29 property bool available: true // can be used to disable all interactions
30 property alias inverted: panel.inverted
31 property bool shadeBackground: true // can be used to disable background shade when launcher is visible
33 property int panelWidth: units.gu(10)
34 property int dragAreaWidth: units.gu(1)
35 property int minimizeDistance: units.gu(26)
36 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
37 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
39 property bool superPressed: false
40 property bool superTabPressed: false
42 readonly property bool dragging: dragArea.dragging
43 readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
44 readonly property real visibleWidth: panel.width + panel.x
46 readonly property bool shown: panel.x > -panel.width
48 // emitted when an application is selected
49 signal launcherApplicationSelected(string appId)
51 // emitted when the apps dash should be shown because of a swipe gesture
54 // emitted when the dash icon in the launcher has been tapped
59 panel.dismissTimer.stop()
61 panel.dismissTimer.restart()
65 onSuperPressedChanged: {
67 superPressTimer.start();
68 superLongPressTimer.start();
70 superPressTimer.stop();
71 superLongPressTimer.stop();
72 launcher.switchToNextState("");
73 panel.shortcutHintsShown = false;
77 onSuperTabPressedChanged: {
78 if (superTabPressed) {
79 switchToNextState("visible")
80 panel.highlightIndex = -1;
82 superPressTimer.stop();
83 superLongPressTimer.stop();
85 if (panel.highlightIndex == -1) {
87 } else if (panel.highlightIndex >= 0){
88 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
90 panel.highlightIndex = -2;
91 switchToNextState("");
96 onLockedVisibleChanged: {
97 if (lockedVisible && state == "") {
98 panel.dismissTimer.stop();
99 fadeOutAnimation.stop();
100 switchToNextState("visible")
101 } else if (!lockedVisible && state == "visible") {
107 switchToNextState("")
111 if (!root.lockedVisible) {
112 fadeOutAnimation.start();
116 function switchToNextState(state) {
117 animateTimer.nextState = state
118 animateTimer.start();
122 if (available && !dragArea.dragging) {
123 teaseTimer.mode = "teasing"
129 if (available && root.state == "") {
130 teaseTimer.mode = "hinting"
135 function pushEdge(amount) {
136 if (root.state === "") {
137 edgeBarrier.push(amount);
141 function openForKeyboardNavigation() {
142 panel.highlightIndex = -1; // The BFB
144 switchToNextState("visible")
150 panel.highlightPrevious();
151 event.accepted = true;
155 panel.highlightNext()
157 panel.highlightPrevious();
159 event.accepted = true;
162 panel.highlightNext();
163 event.accepted = true;
167 panel.highlightPrevious();
169 panel.highlightNext();
171 event.accepted = true;
174 panel.openQuicklist(panel.highlightIndex)
175 event.accepted = true;
178 panel.highlightIndex = -2;
179 // Falling through intentionally
183 if (panel.highlightIndex == -1) {
185 } else if (panel.highlightIndex >= 0) {
186 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
189 panel.highlightIndex = -2
190 event.accepted = true;
199 switchToNextState("visible")
204 id: superLongPressTimer
207 switchToNextState("visible")
208 panel.shortcutHintsShown = true;
214 interval: mode == "teasing" ? 200 : 300
215 property string mode: "teasing"
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
224 objectName: "animateTimer"
226 property string nextState: ""
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"
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
238 root.state = nextState
243 target: LauncherModel
249 onLanguageChanged: LauncherModel.refresh()
252 SequentialAnimation {
256 animateTimer.stop(); // Don't change the state behind our back
257 panel.layer.enabled = true
260 UbuntuNumberAnimation {
263 easing.type: Easing.InQuad
268 panel.layer.enabled = false
269 panel.animate = false;
271 panel.x = -panel.width
273 panel.animate = true;
280 enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary") && !root.lockedVisible
282 anchors.rightMargin: -units.gu(2)
290 if (panel.x < -panel.width/3) {
291 root.switchToNextState("")
293 root.switchToNextState("visible")
301 enabled: root.shadeBackground && root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
304 panel.highlightIndex = -2
313 opacity: root.shadeBackground && root.state == "visible" && !root.lockedVisible ? 0.6 : 0
315 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
322 enabled: root.available
323 onPassed: { root.switchToNextState("visibleTemporary"); }
324 material: Component {
330 anchors.centerIn: parent
332 GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
333 GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
342 objectName: "launcherPanel"
343 enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
344 width: root.panelWidth
347 bottom: parent.bottom
350 visible: root.x > 0 || x > -width || dragArea.pressed
353 property var dismissTimer: Timer { interval: 500 }
355 target: panel.dismissTimer
357 if (root.autohideEnabled && !root.lockedVisible) {
358 if (!panel.preventHiding) {
361 panel.dismissTimer.restart()
367 property bool animate: true
369 onApplicationSelected: {
371 launcherApplicationSelected(appId)
378 onPreventHidingChanged: {
379 if (panel.dismissTimer.running) {
380 panel.dismissTimer.restart();
384 onKbdNavigationCancelled: {
385 panel.highlightIndex = -2;
391 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
394 easing.type: Easing.OutCubic
398 Behavior on opacity {
400 duration: UbuntuAnimation.FastDuration; easing.type: Easing.OutCubic
405 DirectionalDragArea {
407 objectName: "launcherDragArea"
409 direction: Direction.Rightwards
411 enabled: root.available
412 x: -root.x // so if launcher is adjusted relative to screen, we stay put (like tutorial does when teasing)
413 width: root.dragAreaWidth
417 if (!dragging || launcher.state == "visible")
420 panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
425 if (distance > panel.width / 2) {
426 root.switchToNextState("visible")
427 if (distance > minimizeDistance) {
430 } else if (root.state === "") {
431 // didn't drag far enough. rollback
432 root.switchToNextState("")
440 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
450 x: -root.x // so we never go past panelWidth, even when teased by tutorial
454 name: "visibleTemporary"
458 autohideEnabled: true
463 when: teaseTimer.running && teaseTimer.mode == "teasing"
466 x: -root.panelWidth + units.gu(2)
471 when: teaseTimer.running && teaseTimer.mode == "hinting"