@@ -19,18 +19,23 @@ import "../Components"
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Ubuntu.Gestures 0.1
20import Ubuntu.Gestures 0.1
21import Unity.Launcher 0.1
21import Unity.Launcher 0.1
22+import Utils 0.1 as Utils
23FocusScope {
24FocusScope {
27+ readonly property int ignoreHideIfMouseOverLauncher: 1
26 property bool autohideEnabled: false
29 property bool autohideEnabled: false
27 property bool lockedVisible: false
30 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
31 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
32 property alias inverted: panel.inverted
33+ property Item blurSource: null
34+ property int topPanelHeight: 0
35+ property bool drawerEnabled: true
31 property int panelWidth: units.gu(10)
37 property int panelWidth: units.gu(10)
32 property int dragAreaWidth: units.gu(1)
38 property int dragAreaWidth: units.gu(1)
33− property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
39 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
35 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
40 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
@@ -43,13 +48,11 @@ FocusScope {
43 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
48 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
45 readonly property bool shown: panel.x > -panel.width
50 readonly property bool shown: panel.x > -panel.width
51+ readonly property bool drawerShown: drawer.x == 0
47 // emitted when an application is selected
53 // emitted when an application is selected
48 signal launcherApplicationSelected(string appId)
54 signal launcherApplicationSelected(string appId)
50− // emitted when the apps dash should be shown because of a swipe gesture
53 // emitted when the dash icon in the launcher has been tapped
56 // emitted when the dash icon in the launcher has been tapped
54 signal showDashHome()
57 signal showDashHome()
@@ -62,6 +65,9 @@ FocusScope {
64 onSuperPressedChanged: {
67 onSuperPressedChanged: {
68+ if (state == "drawer")
65 if (superPressed) {
71 if (superPressed) {
66 superPressTimer.start();
72 superPressTimer.start();
67 superLongPressTimer.start();
73 superLongPressTimer.start();
@@ -102,7 +108,10 @@ FocusScope {
105− function hide() {
111+ function hide(flags) {
112+ if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
106 switchToNextState("")
115 switchToNextState("")
@@ -132,7 +141,7 @@ FocusScope {
134 function pushEdge(amount) {
143 function pushEdge(amount) {
135− if (root.state === "") {
144+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
136 edgeBarrier.push(amount);
145 edgeBarrier.push(amount);
@@ -143,6 +152,22 @@ FocusScope {
143 switchToNextState("visible")
152 switchToNextState("visible")
155+ function openDrawer(focusInputField) {
156+ if (!drawerEnabled) {
160+ panel.shortcutHintsShown = false;
161+ superPressTimer.stop();
162+ superLongPressTimer.stop();
164+ drawer.focus = true;
165+ if (focusInputField) {
166+ drawer.focusInput();
168+ switchToNextState("drawer")
146 Keys.onPressed: {
171 Keys.onPressed: {
147 switch (event.key) {
172 switch (event.key) {
148 case Qt.Key_Backtab:
173 case Qt.Key_Backtab:
@@ -278,7 +303,7 @@ FocusScope {
278 InverseMouseArea {
303 InverseMouseArea {
279 id: closeMouseArea
304 id: closeMouseArea
280 anchors.fill: panel
305 anchors.fill: panel
281− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
306+ enabled: root.state == "visible" || root.state == "drawer"
282 visible: enabled
307 visible: enabled
283 onPressed: {
308 onPressed: {
284 mouse.accepted = false;
309 mouse.accepted = false;
@@ -307,23 +332,61 @@ FocusScope {
310− EdgeBarrier {
335+ BackgroundBlur {
311− id: edgeBarrier
336+ id: backgroundBlur
312− edge: Qt.LeftEdge
337+ anchors.fill: parent
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,
343+ root.topPanelHeight,
344+ drawer.width + drawer.x - panel.width,
345+ height - root.topPanelHeight)
346+ cached: drawer.moving
351+ objectName: "drawer"
313− target: parent
353+ top: parent.top
314− enabled: root.available
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: {
315− onPassed: { root.switchToNextState("visibleTemporary"); }
377+ switchToNextState("");
316− material: Component {
378+ root.focus = false;
319− width: parent.height
381+ onDragDistanceChanged: {
320− height: parent.width
382+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
322− anchors.centerIn: parent
384+ onDraggingHorizontallyChanged: {
323− gradient: Gradient {
385+ if (!draggingHorizontally) {
324− GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
386+ if (drawer.x < -units.gu(10)) {
325− GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
387+ root.hide();
@@ -332,7 +395,7 @@ FocusScope {
332 LauncherPanel {
395 LauncherPanel {
333 id: panel
396 id: panel
334 objectName: "launcherPanel"
397 objectName: "launcherPanel"
335− enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
398+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
336 width: root.panelWidth
399 width: root.panelWidth
337 anchors {
400 anchors {
338 top: parent.top
401 top: parent.top
@@ -359,11 +422,11 @@ FocusScope {
359 property bool animate: true
422 property bool animate: true
361 onApplicationSelected: {
424 onApplicationSelected: {
362− root.hide();
425+ root.hide(ignoreHideIfMouseOverLauncher);
363 launcherApplicationSelected(appId)
426 launcherApplicationSelected(appId)
365 onShowDashHome: {
428 onShowDashHome: {
366− root.hide();
429+ root.hide(ignoreHideIfMouseOverLauncher);
367 root.showDashHome();
430 root.showDashHome();
@@ -394,6 +457,38 @@ FocusScope {
464+ enabled: root.available
465+ onProgressChanged: {
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 {
479+ width: parent.height
480+ height: parent.width
482+ anchors.centerIn: parent
483+ gradient: Gradient {
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)}
397 SwipeArea {
492 SwipeArea {
398 id: dragArea
493 id: dragArea
399 objectName: "launcherDragArea"
494 objectName: "launcherDragArea"
@@ -405,25 +500,62 @@ FocusScope {
405 width: root.dragAreaWidth
500 width: root.dragAreaWidth
406 height: root.height
501 height: root.height
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";
408 onDistanceChanged: {
525 onDistanceChanged: {
409− if (!dragging || launcher.state == "visible")
526+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
410− return;
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);
412− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.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));
415 onDraggingChanged: {
544 onDraggingChanged: {
416 if (!dragging) {
545 if (!dragging) {
417 if (distance > panel.width / 2) {
546 if (distance > panel.width / 2) {
418− root.switchToNextState("visible")
547+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
419− if (distance > minimizeDistance) {
548+ root.switchToNextState("drawer");
420− root.dash();
549+ root.focus = true;
551+ root.switchToNextState("visible");
422 } else if (root.state === "") {
553 } else if (root.state === "") {
423 // didn't drag far enough. rollback
554 // didn't drag far enough. rollback
424− root.switchToNextState("")
555+ root.switchToNextState("");
558+ lastDragPoints = [];
@@ -434,6 +566,10 @@ FocusScope {
434 target: panel
566 target: panel
435 x: -root.panelWidth
567 x: -root.panelWidth
571+ anchors.rightMargin: 0
439 name: "visible"
575 name: "visible"
@@ -441,6 +577,18 @@ FocusScope {
441 target: panel
577 target: panel
442 x: -root.x // so we never go past panelWidth, even when teased by tutorial
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
446 name: "visibleTemporary"
594 name: "visibleTemporary"