@@ -27,10 +27,12 @@ FocusScope {
2727 property bool lockedVisible: false
2828 property bool available: true // can be used to disable all interactions
2929 property alias inverted: panel.inverted
30+ property Item blurSource: null
31+ property int topPanelHeight: 0
32+ property bool drawerEnabled: true
3033
3134 property int panelWidth: units.gu(10)
3235 property int dragAreaWidth: units.gu(1)
33− property int minimizeDistance: units.gu(26)
3436 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
3537 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
3638
@@ -43,13 +45,11 @@ FocusScope {
4345 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
4446
4547 readonly property bool shown: panel.x > -panel.width
48+ readonly property bool drawerShown: drawer.x == 0
4649
4750 // emitted when an application is selected
4851 signal launcherApplicationSelected(string appId)
4952
50− // emitted when the apps dash should be shown because of a swipe gesture
51− signal dash()
52−
5353 // emitted when the dash icon in the launcher has been tapped
5454 signal showDashHome()
5555
@@ -62,6 +62,9 @@ FocusScope {
6262 }
6363
6464 onSuperPressedChanged: {
65+ if (state == "drawer")
66+ return;
67+
6568 if (superPressed) {
6669 superPressTimer.start();
6770 superLongPressTimer.start();
@@ -132,7 +135,7 @@ FocusScope {
132135 }
133136
134137 function pushEdge(amount) {
135− if (root.state === "") {
138+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
136139 edgeBarrier.push(amount);
137140 }
138141 }
@@ -143,6 +146,22 @@ FocusScope {
143146 switchToNextState("visible")
144147 }
145148
149+ function openDrawer(focusInputField) {
150+ if (!drawerEnabled) {
151+ return;
152+ }
153+
154+ panel.shortcutHintsShown = false;
155+ superPressTimer.stop();
156+ superLongPressTimer.stop();
157+ root.focus = true;
158+ drawer.focus = true;
159+ if (focusInputField) {
160+ drawer.focusInput();
161+ }
162+ switchToNextState("drawer")
163+ }
164+
146165 Keys.onPressed: {
147166 switch (event.key) {
148167 case Qt.Key_Backtab:
@@ -278,7 +297,7 @@ FocusScope {
278297 InverseMouseArea {
279298 id: closeMouseArea
280299 anchors.fill: panel
281− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
300+ enabled: root.state == "visible" || root.state == "drawer"
282301 visible: enabled
283302 onPressed: {
284303 mouse.accepted = false;
@@ -307,23 +326,61 @@ FocusScope {
307326 }
308327 }
309328
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− }
329+ BackgroundBlur {
330+ id: backgroundBlur
331+ anchors.fill: parent
332+ anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
333+ visible: root.blurSource && drawer.x > -drawer.width
334+ blurAmount: units.gu(6)
335+ sourceItem: root.blurSource
336+ blurRect: Qt.rect(panel.width,
337+ root.topPanelHeight,
338+ drawer.width + drawer.x - panel.width,
339+ height - root.topPanelHeight)
340+ cached: drawer.moving
341+ }
342+
343+ Drawer {
344+ id: drawer
345+ objectName: "drawer"
346+ anchors {
347+ top: parent.top
348+ topMargin: root.inverted ? root.topPanelHeight : 0
349+ bottom: parent.bottom
350+ right: parent.left
351+ }
352+ width: Math.min(root.width, units.gu(90)) * .9
353+ panelWidth: panel.width
354+ visible: x > -width
355+
356+ Behavior on anchors.rightMargin {
357+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
358+ NumberAnimation {
359+ duration: 300
360+ easing.type: Easing.OutCubic
361+ }
362+ }
363+
364+ onApplicationSelected: {
365+ root.hide();
366+ root.launcherApplicationSelected(appId)
367+ root.focus = false;
368+ }
369+
370+ Keys.onEscapePressed: {
371+ switchToNextState("");
372+ root.focus = false;
373+ }
374+
375+ onDragDistanceChanged: {
376+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
377+ }
378+ onDraggingHorizontallyChanged: {
379+ if (!draggingHorizontally) {
380+ if (drawer.x < -units.gu(10)) {
381+ root.hide();
382+ } else {
383+ root.openDrawer();
327384 }
328385 }
329386 }
@@ -332,7 +389,7 @@ FocusScope {
332389 LauncherPanel {
333390 id: panel
334391 objectName: "launcherPanel"
335− enabled: root.available && root.state == "visible" || root.state == "visibleTemporary"
392+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary" || root.state == "drawer")
336393 width: root.panelWidth
337394 anchors {
338395 top: parent.top
@@ -394,6 +451,38 @@ FocusScope {
394451 }
395452 }
396453
454+ EdgeBarrier {
455+ id: edgeBarrier
456+ edge: Qt.LeftEdge
457+ target: parent
458+ enabled: root.available
459+ onProgressChanged: {
460+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
461+ root.switchToNextState("visibleTemporary");
462+ }
463+ }
464+ onPassed: {
465+ if (root.drawerEnabled) {
466+ root.switchToNextState("drawer");
467+ }
468+ }
469+
470+ material: Component {
471+ Item {
472+ Rectangle {
473+ width: parent.height
474+ height: parent.width
475+ rotation: -90
476+ anchors.centerIn: parent
477+ gradient: Gradient {
478+ GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
479+ GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
480+ }
481+ }
482+ }
483+ }
484+ }
485+
397486 SwipeArea {
398487 id: dragArea
399488 objectName: "launcherDragArea"
@@ -405,19 +494,34 @@ FocusScope {
405494 width: root.dragAreaWidth
406495 height: root.height
407496
497+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
498+
408499 onDistanceChanged: {
409− if (!dragging || launcher.state == "visible")
410− return;
500+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
501+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
502+ }
411503
412− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
504+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
505+ var drawerHintDistance = panel.width + units.gu(1)
506+ if (distance < drawerHintDistance) {
507+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
508+ } else {
509+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
510+ var linearDrawerProgress = linearDrawerX / (drawer.width)
511+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
512+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
513+ }
514+ }
413515 }
414516
415517 onDraggingChanged: {
416518 if (!dragging) {
417519 if (distance > panel.width / 2) {
418− root.switchToNextState("visible")
419− if (distance > minimizeDistance) {
420− root.dash();
520+ if (root.drawerEnabled && distance > panel.width * 3) {
521+ root.switchToNextState("drawer")
522+ root.focus = true;
523+ } else {
524+ root.switchToNextState("visible")
421525 }
422526 } else if (root.state === "") {
423527 // didn't drag far enough. rollback
@@ -434,6 +538,10 @@ FocusScope {
434538 target: panel
435539 x: -root.panelWidth
436540 }
541+ PropertyChanges {
542+ target: drawer
543+ anchors.rightMargin: 0
544+ }
437545 },
438546 State {
439547 name: "visible"
@@ -441,6 +549,18 @@ FocusScope {
441549 target: panel
442550 x: -root.x // so we never go past panelWidth, even when teased by tutorial
443551 }
552+ PropertyChanges {
553+ target: drawer
554+ anchors.rightMargin: 0
555+ }
556+ },
557+ State {
558+ name: "drawer"
559+ extend: "visible"
560+ PropertyChanges {
561+ target: drawer
562+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
563+ }
444564 },
445565 State {
446566 name: "visibleTemporary"