@@ -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();
@@ -106,7 +112,10 @@ FocusScope {
109− function hide() {
115+ function hide(flags) {
116+ if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
110 switchToNextState("")
119 switchToNextState("")
@@ -136,7 +145,7 @@ FocusScope {
138 function pushEdge(amount) {
147 function pushEdge(amount) {
139− if (root.state === "") {
148+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
140 edgeBarrier.push(amount);
149 edgeBarrier.push(amount);
@@ -147,6 +156,22 @@ FocusScope {
147 switchToNextState("visible")
156 switchToNextState("visible")
159+ function openDrawer(focusInputField) {
160+ if (!drawerEnabled) {
164+ panel.shortcutHintsShown = false;
165+ superPressTimer.stop();
166+ superLongPressTimer.stop();
168+ drawer.focus = true;
169+ if (focusInputField) {
170+ drawer.focusInput();
172+ switchToNextState("drawer")
150 Keys.onPressed: {
175 Keys.onPressed: {
151 switch (event.key) {
176 switch (event.key) {
152 case Qt.Key_Backtab:
177 case Qt.Key_Backtab:
@@ -282,7 +307,7 @@ FocusScope {
282 InverseMouseArea {
307 InverseMouseArea {
283 id: closeMouseArea
308 id: closeMouseArea
284 anchors.fill: panel
309 anchors.fill: panel
285− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
310+ enabled: root.state == "visible" || root.state == "drawer"
286 visible: enabled
311 visible: enabled
287 onPressed: {
312 onPressed: {
288 mouse.accepted = false;
313 mouse.accepted = false;
@@ -311,23 +336,61 @@ FocusScope {
314− EdgeBarrier {
339+ BackgroundBlur {
315− id: edgeBarrier
340+ id: backgroundBlur
316− edge: Qt.LeftEdge
341+ anchors.fill: parent
342+ anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
343+ visible: root.blurSource && drawer.x > -drawer.width
344+ blurAmount: units.gu(6)
345+ sourceItem: root.blurSource
346+ blurRect: Qt.rect(panel.width,
347+ root.topPanelHeight,
348+ drawer.width + drawer.x - panel.width,
349+ height - root.topPanelHeight)
350+ cached: drawer.moving
355+ objectName: "drawer"
317− target: parent
357+ top: parent.top
318− enabled: root.available
358+ topMargin: root.inverted ? root.topPanelHeight : 0
359+ bottom: parent.bottom
362+ width: Math.min(root.width, units.gu(90)) * .9
363+ panelWidth: panel.width
366+ Behavior on anchors.rightMargin {
367+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
370+ easing.type: Easing.OutCubic
374+ onApplicationSelected: {
376+ root.launcherApplicationSelected(appId)
380+ Keys.onEscapePressed: {
319− onPassed: { root.switchToNextState("visibleTemporary"); }
381+ switchToNextState("");
320− material: Component {
382+ root.focus = false;
323− width: parent.height
385+ onDragDistanceChanged: {
324− height: parent.width
386+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
326− anchors.centerIn: parent
388+ onDraggingHorizontallyChanged: {
327− gradient: Gradient {
389+ if (!draggingHorizontally) {
328− GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
390+ if (drawer.x < -units.gu(10)) {
329− GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
391+ root.hide();
@@ -336,7 +399,7 @@ FocusScope {
336 LauncherPanel {
399 LauncherPanel {
337 id: panel
400 id: panel
338 objectName: "launcherPanel"
401 objectName: "launcherPanel"
339− enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
402+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
340 width: root.panelWidth
403 width: root.panelWidth
341 anchors {
404 anchors {
342 top: parent.top
405 top: parent.top
@@ -363,11 +426,11 @@ FocusScope {
363 property bool animate: true
426 property bool animate: true
365 onApplicationSelected: {
428 onApplicationSelected: {
366− root.hide();
429+ root.hide(ignoreHideIfMouseOverLauncher);
367 launcherApplicationSelected(appId)
430 launcherApplicationSelected(appId)
369 onShowDashHome: {
432 onShowDashHome: {
370− root.hide();
433+ root.hide(ignoreHideIfMouseOverLauncher);
371 root.showDashHome();
434 root.showDashHome();
@@ -398,6 +461,38 @@ FocusScope {
468+ enabled: root.available
469+ onProgressChanged: {
470+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
471+ root.switchToNextState("visibleTemporary");
475+ if (root.drawerEnabled) {
476+ root.switchToNextState("drawer");
480+ material: Component {
483+ width: parent.height
484+ height: parent.width
486+ anchors.centerIn: parent
487+ gradient: Gradient {
488+ GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
489+ GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
401 SwipeArea {
496 SwipeArea {
402 id: dragArea
497 id: dragArea
403 objectName: "launcherDragArea"
498 objectName: "launcherDragArea"
@@ -409,25 +504,62 @@ FocusScope {
409 width: root.dragAreaWidth
504 width: root.dragAreaWidth
410 height: root.height
505 height: root.height
507+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
509+ property var lastDragPoints: []
511+ function dragDirection() {
512+ if (lastDragPoints.length < 5) {
518+ for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
519+ if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
522+ if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
526+ return toRight ? "right" : toLeft ? "left" : "unknown";
412 onDistanceChanged: {
529 onDistanceChanged: {
413− if (!dragging || launcher.state == "visible")
530+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
414− return;
531+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
534+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
535+ lastDragPoints.push(distance)
536+ var drawerHintDistance = panel.width + units.gu(1)
537+ if (distance < drawerHintDistance) {
538+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
416− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
540+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
541+ var linearDrawerProgress = linearDrawerX / (drawer.width)
542+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
543+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
419 onDraggingChanged: {
548 onDraggingChanged: {
420 if (!dragging) {
549 if (!dragging) {
421 if (distance > panel.width / 2) {
550 if (distance > panel.width / 2) {
422− root.switchToNextState("visible")
551+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
423− if (distance > minimizeDistance) {
552+ root.switchToNextState("drawer");
424− root.dash();
553+ root.focus = true;
555+ root.switchToNextState("visible");
426 } else if (root.state === "") {
557 } else if (root.state === "") {
427 // didn't drag far enough. rollback
558 // didn't drag far enough. rollback
428− root.switchToNextState("")
559+ root.switchToNextState("");
562+ lastDragPoints = [];
@@ -438,6 +570,10 @@ FocusScope {
438 target: panel
570 target: panel
439 x: -root.panelWidth
571 x: -root.panelWidth
575+ anchors.rightMargin: 0
443 name: "visible"
579 name: "visible"
@@ -445,6 +581,18 @@ FocusScope {
445 target: panel
581 target: panel
446 x: -root.x // so we never go past panelWidth, even when teased by tutorial
582 x: -root.x // so we never go past panelWidth, even when teased by tutorial
586+ anchors.rightMargin: 0
594+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
450 name: "visibleTemporary"
598 name: "visibleTemporary"