@@ -27,10 +27,12 @@ FocusScope {
27 property bool lockedVisible: false
27 property bool lockedVisible: false
28 property bool available: true // can be used to disable all interactions
28 property bool available: true // can be used to disable all interactions
29 property alias inverted: panel.inverted
29 property alias inverted: panel.inverted
30+ property Item blurSource: null
31+ property int topPanelHeight: 0
32+ property bool drawerEnabled: true
31 property int panelWidth: units.gu(10)
34 property int panelWidth: units.gu(10)
32 property int dragAreaWidth: units.gu(1)
35 property int dragAreaWidth: units.gu(1)
33− property int minimizeDistance: units.gu(26)
34 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
36 property real progress: dragArea.dragging && dragArea.touchPosition.x > panelWidth ?
35 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
37 (width * (dragArea.touchPosition.x-panelWidth) / (width - panelWidth)) : 0
@@ -43,13 +45,11 @@ FocusScope {
43 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
45 readonly property alias shortcutHintsShown: panel.shortcutHintsShown
45 readonly property bool shown: panel.x > -panel.width
47 readonly property bool shown: panel.x > -panel.width
48+ readonly property bool drawerShown: drawer.x == 0
47 // emitted when an application is selected
50 // emitted when an application is selected
48 signal launcherApplicationSelected(string appId)
51 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
53 // emitted when the dash icon in the launcher has been tapped
54 signal showDashHome()
54 signal showDashHome()
@@ -62,6 +62,9 @@ FocusScope {
64 onSuperPressedChanged: {
64 onSuperPressedChanged: {
65+ if (state == "drawer")
65 if (superPressed) {
68 if (superPressed) {
66 superPressTimer.start();
69 superPressTimer.start();
67 superLongPressTimer.start();
70 superLongPressTimer.start();
@@ -132,7 +135,7 @@ FocusScope {
134 function pushEdge(amount) {
137 function pushEdge(amount) {
135− if (root.state === "") {
138+ if (root.state === "" || root.state == "visible" || root.state == "visibleTemporary") {
136 edgeBarrier.push(amount);
139 edgeBarrier.push(amount);
@@ -143,6 +146,22 @@ FocusScope {
143 switchToNextState("visible")
146 switchToNextState("visible")
149+ function openDrawer(focusInputField) {
150+ if (!drawerEnabled) {
154+ panel.shortcutHintsShown = false;
155+ superPressTimer.stop();
156+ superLongPressTimer.stop();
158+ drawer.focus = true;
159+ if (focusInputField) {
160+ drawer.focusInput();
162+ switchToNextState("drawer")
146 Keys.onPressed: {
165 Keys.onPressed: {
147 switch (event.key) {
166 switch (event.key) {
148 case Qt.Key_Backtab:
167 case Qt.Key_Backtab:
@@ -278,7 +297,7 @@ FocusScope {
278 InverseMouseArea {
297 InverseMouseArea {
279 id: closeMouseArea
298 id: closeMouseArea
280 anchors.fill: panel
299 anchors.fill: panel
281− enabled: root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
300+ enabled: root.state == "visible" || root.state == "drawer"
282 visible: enabled
301 visible: enabled
283 onPressed: {
302 onPressed: {
284 mouse.accepted = false;
303 mouse.accepted = false;
@@ -307,23 +326,61 @@ FocusScope {
310− EdgeBarrier {
329+ BackgroundBlur {
311− id: edgeBarrier
330+ id: backgroundBlur
312− edge: Qt.LeftEdge
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
345+ objectName: "drawer"
313− target: parent
347+ top: parent.top
314− enabled: root.available
348+ topMargin: root.inverted ? root.topPanelHeight : 0
349+ bottom: parent.bottom
352+ width: Math.min(root.width, units.gu(90)) * .9
353+ panelWidth: panel.width
356+ Behavior on anchors.rightMargin {
357+ enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate && !drawer.draggingHorizontally
360+ easing.type: Easing.OutCubic
364+ onApplicationSelected: {
366+ root.launcherApplicationSelected(appId)
370+ Keys.onEscapePressed: {
315− onPassed: { root.switchToNextState("visibleTemporary"); }
371+ switchToNextState("");
316− material: Component {
372+ root.focus = false;
319− width: parent.height
375+ onDragDistanceChanged: {
320− height: parent.width
376+ anchors.rightMargin = Math.max(-drawer.width, anchors.rightMargin + dragDistance);
322− anchors.centerIn: parent
378+ onDraggingHorizontallyChanged: {
323− gradient: Gradient {
379+ if (!draggingHorizontally) {
324− GradientStop { position: 0.0; color: Qt.rgba(panel.color.r, panel.color.g, panel.color.b, .5)}
380+ if (drawer.x < -units.gu(10)) {
325− GradientStop { position: 1.0; color: Qt.rgba(panel.color.r,panel.color.g,panel.color.b,0)}
381+ root.hide();
@@ -332,7 +389,7 @@ FocusScope {
332 LauncherPanel {
389 LauncherPanel {
333 id: panel
390 id: panel
334 objectName: "launcherPanel"
391 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")
336 width: root.panelWidth
393 width: root.panelWidth
337 anchors {
394 anchors {
338 top: parent.top
395 top: parent.top
@@ -394,6 +451,38 @@ FocusScope {
458+ enabled: root.available
459+ onProgressChanged: {
460+ if (progress > .5 && root.state != "visibleTemporary" && root.state != "drawer" && root.state != "visible") {
461+ root.switchToNextState("visibleTemporary");
465+ if (root.drawerEnabled) {
466+ root.switchToNextState("drawer");
470+ material: Component {
473+ width: parent.height
474+ height: parent.width
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)}
397 SwipeArea {
486 SwipeArea {
398 id: dragArea
487 id: dragArea
399 objectName: "launcherDragArea"
488 objectName: "launcherDragArea"
@@ -405,25 +494,62 @@ FocusScope {
405 width: root.dragAreaWidth
494 width: root.dragAreaWidth
406 height: root.height
495 height: root.height
497+ function easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }
499+ property var lastDragPoints: []
501+ function dragDirection() {
502+ if (lastDragPoints.length < 5) {
508+ for (var i = lastDragPoints.length - 5; i < lastDragPoints.length; i++) {
509+ if (toRight && lastDragPoints[i] < lastDragPoints[i-1]) {
512+ if (toLeft && lastDragPoints[i] > lastDragPoints[i-1]) {
516+ return toRight ? "right" : toLeft ? "left" : "unknown";
408 onDistanceChanged: {
519 onDistanceChanged: {
409− if (!dragging || launcher.state == "visible")
520+ if (dragging && launcher.state != "visible" && launcher.state != "drawer") {
410− return;
521+ panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
524+ if (root.drawerEnabled && dragging && launcher.state != "drawer") {
525+ lastDragPoints.push(distance)
526+ var drawerHintDistance = panel.width + units.gu(1)
527+ if (distance < drawerHintDistance) {
528+ drawer.anchors.rightMargin = -Math.min(Math.max(0, distance), drawer.width);
412− panel.x = -panel.width + Math.min(Math.max(0, distance), panel.width);
530+ var linearDrawerX = Math.min(Math.max(0, distance - drawerHintDistance), drawer.width);
531+ var linearDrawerProgress = linearDrawerX / (drawer.width)
532+ var easedDrawerProgress = easeInOutCubic(linearDrawerProgress);
533+ drawer.anchors.rightMargin = -(drawerHintDistance + easedDrawerProgress * (drawer.width - drawerHintDistance));
415 onDraggingChanged: {
538 onDraggingChanged: {
416 if (!dragging) {
539 if (!dragging) {
417 if (distance > panel.width / 2) {
540 if (distance > panel.width / 2) {
418− root.switchToNextState("visible")
541+ if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
419− if (distance > minimizeDistance) {
542+ root.switchToNextState("drawer");
420− root.dash();
543+ root.focus = true;
545+ root.switchToNextState("visible");
422 } else if (root.state === "") {
547 } else if (root.state === "") {
423 // didn't drag far enough. rollback
548 // didn't drag far enough. rollback
424− root.switchToNextState("")
549+ root.switchToNextState("");
552+ lastDragPoints = [];
@@ -434,6 +560,10 @@ FocusScope {
434 target: panel
560 target: panel
435 x: -root.panelWidth
561 x: -root.panelWidth
565+ anchors.rightMargin: 0
439 name: "visible"
569 name: "visible"
@@ -441,6 +571,18 @@ FocusScope {
441 target: panel
571 target: panel
442 x: -root.x // so we never go past panelWidth, even when teased by tutorial
572 x: -root.x // so we never go past panelWidth, even when teased by tutorial
576+ anchors.rightMargin: 0
584+ anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
446 name: "visibleTemporary"
588 name: "visibleTemporary"