From 4fd7c1a92e3c16b300997e2cbd82ae3cdd6279ec Mon Sep 17 00:00:00 2001
From: "kali kaneko (leap communications)" <kali@leap.se>
Date: Wed, 8 Dec 2021 22:23:46 +0100
Subject: [PATCH] [bug] alternative drawer implementation

this is an attempt to solve the problem we're having with snaps, in
which Overlays are not correctly displayed.
---
 gui/components/Footer.qml           |   3 +
 gui/components/Header.qml           |   2 +-
 gui/components/MainView.qml         | 126 ++++++++++----------
 gui/components/MaterialButton.qml   |   7 +-
 gui/components/NavigationDrawer.qml | 177 ++++++++++++++++++++++++++++
 gui/components/StatusBox.qml        |   8 +-
 gui/gui.qrc                         |   1 +
 gui/main.qml                        |   1 +
 8 files changed, 256 insertions(+), 69 deletions(-)
 create mode 100644 gui/components/NavigationDrawer.qml

diff --git a/gui/components/Footer.qml b/gui/components/Footer.qml
index e6eb2643..5e8335e4 100644
--- a/gui/components/Footer.qml
+++ b/gui/components/Footer.qml
@@ -221,6 +221,9 @@ ToolBar {
     }
 
     function isFooterVisible() {
+        if (drawerOn) {
+            return false
+        }
         if (stackView.depth > 1) {
             return false
         }
diff --git a/gui/components/Header.qml b/gui/components/Header.qml
index ee2d8861..6a1f6aae 100644
--- a/gui/components/Header.qml
+++ b/gui/components/Header.qml
@@ -29,7 +29,7 @@ ToolBar {
             if (stackView.depth > 1) {
                 stackView.pop()
             } else {
-                settingsDrawer.open()
+                settingsDrawer.toggle()
             }
         }
     }
diff --git a/gui/components/MainView.qml b/gui/components/MainView.qml
index 0f5fb58e..886e8964 100644
--- a/gui/components/MainView.qml
+++ b/gui/components/MainView.qml
@@ -7,83 +7,80 @@ import QtQuick.Layouts 1.14
 import "../themes/themes.js" as Theme
 
 Page {
-
     StackView {
         id: stackView
         anchors.fill: parent
         initialItem: Home {}
     }
 
-    Drawer {
+    NavigationDrawer {
         id: settingsDrawer
-
-        width: Math.min(root.width, root.height) / 3 * 2
-        height: root.height
-
-        ListView {
-            focus: true
-            currentIndex: -1
+        Rectangle {
             anchors.fill: parent
-
-            delegate: ItemDelegate {
-                width: parent.width
-                text: model.text
-                visible: {
-                    if (isDonationService) {return true}
-                    return model.text != qsTr("Donate")
-                }
-                highlighted: ListView.isCurrentItem
-                icon.color: "transparent"
-                icon.source: model.icon
-                onClicked: {
-                    settingsDrawer.close()
-                    model.triggered()
-                }
-            }
-
-            model: ListModel {
-                ListElement {
-                    text: qsTr("Preferences")
-                    icon: "../resources/tools.svg"
-                    triggered: function () {
-                        stackView.push("Preferences.qml")
-                    }
-                }
-                ListElement {
-                    text: qsTr("Donate")
-                    icon: "../resources/donate.svg"
-                    triggered: function () {
-                        Qt.openUrlExternally(ctx.donateURL)
-                    }
-                }
-                ListElement {
-                    text: qsTr("Help")
-                    icon: "../resources/help.svg"
-                    triggered: function () {
-                        stackView.push("Help.qml")
-                        settingsDrawer.close()
-                    }
-                } // -> can link to another dialog with report bug / support / contribute / FAQ
-                ListElement {
-                    text: qsTr("About")
-                    icon: "../resources/about.svg"
-                    triggered: function () {
-                        stackView.push("About.qml")
-                        settingsDrawer.close()
+            color: "white"
+            ListView {
+                focus: true
+                currentIndex: -1
+                anchors.fill: parent
+
+                model: navModel
+                delegate: ItemDelegate {
+                    width: parent.width
+                    text: model.text
+                    visible: {
+                        if (isDonationService) {return true}
+                        return model.text != qsTr("Donate")
                     }
-                }
-                ListElement {
-                    text: qsTr("Quit")
-                    icon: "../resources/quit.svg"
-                    triggered: function () {
-                        backend.quit()
+                    highlighted: ListView.isCurrentItem
+                    icon.color: "transparent"
+                    icon.source: model.icon
+                    onClicked: {
+                        settingsDrawer.toggle()
+                        model.triggered()
                     }
                 }
             }
-
-            ScrollIndicator.vertical: ScrollIndicator {}
         }
-    }
+     }
+
+     ListModel {
+        id: navModel
+        ListElement {
+            text: qsTr("Preferences")
+            icon: "../resources/tools.svg"
+            triggered: function () {
+                stackView.push("Preferences.qml")
+            }
+        }
+        ListElement {
+            text: qsTr("Donate")
+            icon: "../resources/donate.svg"
+            triggered: function () {
+                Qt.openUrlExternally(ctx.donateURL)
+            }
+        }
+        ListElement {
+            text: qsTr("Help")
+            icon: "../resources/help.svg"
+            triggered: function () {
+                stackView.push("Help.qml")
+            }
+        } // -> can link to another dialog with report bug / support / contribute / FAQ
+        ListElement {
+            text: qsTr("About")
+            icon: "../resources/about.svg"
+            triggered: function () {
+                stackView.push("About.qml")
+            }
+        }
+        ListElement {
+            text: qsTr("Quit")
+            icon: "../resources/quit.svg"
+            triggered: function () {
+                backend.quit()
+            }
+        }
+    } // end listmodel
 
     header: Header {
         id: header
@@ -146,7 +143,6 @@ Page {
                 horizontalCenter: parent.horizontalCenter
             }
         }
-
         onYes: Qt.openUrlExternally(ctx.donateURL)
     }
 
diff --git a/gui/components/MaterialButton.qml b/gui/components/MaterialButton.qml
index 8e90e94a..ad1ca597 100644
--- a/gui/components/MaterialButton.qml
+++ b/gui/components/MaterialButton.qml
@@ -49,7 +49,7 @@ T.Button {
 
         radius: 4
         border.color: "black"
-        border.width: 1
+        border.width: 2
         color: !control.enabled ? control.Material.buttonDisabledColor : control.highlighted ? control.Material.highlightedButtonColor : Theme.buttonColor
 
         PaddedRectangle {
@@ -67,10 +67,13 @@ T.Button {
         // The layer is disabled when the button color is transparent so you can do
         // Material.background: "transparent" and get a proper flat button without needing
         // to set Material.elevation as well
-        layer.enabled: control.enabled && control.Material.buttonColor.a > 0
+        layer.enabled: true // control.enabled && control.Material.buttonColor.a > 0
+
+        /*
         layer.effect: ElevationEffect {
             elevation: control.Material.elevation
         }
+        */
 
         Ripple {
             clipRadius: 2
diff --git a/gui/components/NavigationDrawer.qml b/gui/components/NavigationDrawer.qml
new file mode 100644
index 00000000..8e7db8e0
--- /dev/null
+++ b/gui/components/NavigationDrawer.qml
@@ -0,0 +1,177 @@
+// (c) Evgenij Legotskoj https://evileg.com/en/post/192/
+// a reimplementation of Drawer to workaround a problem with overlays
+// in the Qt version packaged with the snaps.
+import QtQuick 2.5
+import QtQuick.Window 2.0
+
+Rectangle {
+    id: panel
+
+    function show() {
+        open = true;
+        drawerOn = true;
+    }
+    function hide() {
+        open = false; 
+        drawerOn = false;
+    }
+    function toggle() {
+        open = open ? false : true;
+        drawerOn = open;
+    }
+
+    property bool open: false
+    property int position: Qt.LeftEdge
+
+    readonly property bool _rightEdge: position === Qt.RightEdge
+    readonly property int _closeX: _rightEdge ? _rootItem.width : - panel.width
+    readonly property int _openX: _rightEdge ? _rootItem.width - width : 0
+    readonly property int _minimumX: _rightEdge ? _rootItem.width - panel.width : -panel.width
+    readonly property int _maximumX: _rightEdge ? _rootItem.width : 0
+    readonly property int _pullThreshold: panel.width/2
+    readonly property int _slideDuration: 260
+    readonly property int _openMarginSize: 20
+
+    property real _velocity: 0
+    property real _oldMouseX: -1
+
+    property Item _rootItem: parent
+
+    on_RightEdgeChanged: _setupAnchors()
+    onOpenChanged: completeSlideDirection()
+
+    width: Math.min(root.width, root.height) / 3 * 2
+    height: root.height
+    x: _closeX
+    z: 10
+
+    function _setupAnchors() {
+        _rootItem = parent;
+
+        shadow.anchors.right = undefined;
+        shadow.anchors.left = undefined;
+
+        mouse.anchors.left = undefined;
+        mouse.anchors.right = undefined;
+
+        if (_rightEdge) {
+            mouse.anchors.right = mouse.parent.right;
+            shadow.anchors.right = panel.left;
+        } else {
+            mouse.anchors.left = mouse.parent.left;
+            shadow.anchors.left = panel.right;
+        }
+
+        slideAnimation.enabled = false;
+        panel.x = _rightEdge ? _rootItem.width :  - panel.width;
+        slideAnimation.enabled = true;
+    }
+
+    function completeSlideDirection() {
+        if (open) {
+            panel.x = _openX;
+        } else {
+            panel.x = _closeX;
+            Qt.inputMethod.hide();
+        }
+    }
+
+    function handleRelease() {
+        var velocityThreshold = 5
+        if ((_rightEdge && _velocity > velocityThreshold) ||
+                (!_rightEdge && _velocity < -velocityThreshold)) {
+            panel.open = false;
+            completeSlideDirection()
+        } else if ((_rightEdge && _velocity < -velocityThreshold) ||
+                   (!_rightEdge && _velocity > velocityThreshold)) {
+            panel.open = true;
+            completeSlideDirection()
+        } else if ((_rightEdge && panel.x < _openX + _pullThreshold) ||
+                   (!_rightEdge && panel.x > _openX - _pullThreshold) ) {
+            panel.open = true;
+            panel.x = _openX;
+        } else {
+            panel.open = false;
+            panel.x = _closeX;
+        }
+    }
+
+    function handleClick(mouse) {
+        if ((_rightEdge && mouse.x < panel.x ) || mouse.x > panel.width) {
+            open = false;
+            drawerOn = false;
+        }
+    }
+
+    onPositionChanged: {
+        if (!(position === Qt.RightEdge || position === Qt.LeftEdge )) {
+            console.warn("SlidePanel: Unsupported position.")
+        }
+    }
+
+    Behavior on x {
+        id: slideAnimation
+        enabled: !mouse.drag.active
+        NumberAnimation {
+            duration: _slideDuration
+            easing.type: Easing.OutCubic
+        }
+    }
+
+    NumberAnimation on x {
+        id: holdAnimation
+        to: _closeX + (_openMarginSize * (_rightEdge ? -1 : 1))
+        running : false
+        easing.type: Easing.OutCubic
+        duration: 200
+    }
+
+    MouseArea {
+        id: mouse
+        parent: _rootItem
+
+        y: _rootItem.y
+        width: open ? _rootItem.width : _openMarginSize
+        height: _rootItem.height
+        onPressed:  if (!open) holdAnimation.restart();
+        onClicked: handleClick(mouse)
+        drag.target: panel
+        drag.minimumX: _minimumX
+        drag.maximumX: _maximumX
+        drag.axis: Qt.Horizontal
+        drag.onActiveChanged: if (active) holdAnimation.stop()
+        onReleased: handleRelease()
+        z: open ? 1 : 0
+        onMouseXChanged: {
+            _velocity = (mouse.x - _oldMouseX);
+            _oldMouseX = mouse.x;
+        }
+    }
+
+    Rectangle {
+        id: backgroundBlackout
+        parent: _rootItem
+        anchors.fill: parent
+        opacity: 0.5 * Math.min(1, Math.abs(panel.x - _closeX) / _rootItem.width/2)
+        color: "black"
+    }
+
+    Item {
+        id: shadow
+        anchors.left: panel.right
+        anchors.leftMargin: _rightEdge ? 0 : 10
+        height: parent.height
+
+        Rectangle {
+            height: 10
+            width: panel.height
+            rotation: 90
+            opacity: Math.min(1, Math.abs(panel.x - _closeX)/ _openMarginSize)
+            transformOrigin: Item.TopLeft
+            gradient: Gradient{
+                GradientStop { position: _rightEdge ? 1 : 0 ; color: "#00000000"}
+                GradientStop { position: _rightEdge ? 0 : 1 ; color: "#2c000000"}
+            }
+        }
+    }
+}
diff --git a/gui/components/StatusBox.qml b/gui/components/StatusBox.qml
index 23fc271b..bfcc1753 100644
--- a/gui/components/StatusBox.qml
+++ b/gui/components/StatusBox.qml
@@ -55,11 +55,14 @@ Item {
             cursorShape: Qt.PointingHandCursor
         }
         onClicked: {
+            settingsDrawer.toggle()
+            /*
             if (stackView.depth > 1) {
                 stackView.pop()
             } else {
-                settingsDrawer.open()
+                settingsDrawer.toggle()
             }
+            */
         }
 
         Icon {
@@ -67,8 +70,11 @@ Item {
             width: 16
             height: 16
             anchors.centerIn: settingsButton
+            source: "../resources/gear-fill.svg"
+            /*
             source: stackView.depth
                     > 1 ? "../resources/arrow-left.svg" : "../resources/gear-fill.svg"
+            */
         }
     }
 
diff --git a/gui/gui.qrc b/gui/gui.qrc
index 71a44222..9e357bf3 100644
--- a/gui/gui.qrc
+++ b/gui/gui.qrc
@@ -41,6 +41,7 @@
         <file>components/InitErrors.qml</file>
         <file>components/ErrorBox.qml</file>
         <file>components/MotdBox.qml</file>
+        <file>components/NavigationDrawer.qml</file>
 
         <!-- resources, assets -->
         <file>resources/icon-noshield.svg</file>
diff --git a/gui/main.qml b/gui/main.qml
index 00a84fc9..56220f57 100644
--- a/gui/main.qml
+++ b/gui/main.qml
@@ -14,6 +14,7 @@ ApplicationWindow {
     property int appHeight: 460
     property int appWidth: 280
     property alias customTheme: themeLoader.item
+    property bool drawerOn: false
 
     width: appWidth
     minimumWidth: appWidth
-- 
GitLab