From e534c5d8343c1aba382ccca282c38a4c49ef9d2c Mon Sep 17 00:00:00 2001
From: Sander van Grieken <sander@outrightsolutions.nl>
Date: Mon, 5 Apr 2021 12:24:45 +0200
Subject: [PATCH] qml: switch to QtQuick Controls

---
 electrum/gui/qml/components/EButton.qml       |  41 ------
 electrum/gui/qml/components/EHeader.qml       |  29 ----
 electrum/gui/qml/components/History.qml       | 134 ++++++++++++++++++
 electrum/gui/qml/components/NetworkStats.qml  |  21 +++
 electrum/gui/qml/components/QRScan.qml        |  41 ++++++
 electrum/gui/qml/components/Scan.qml          |  23 +++
 electrum/gui/qml/components/Send.qml          |  69 +++++++++
 .../qml/components/{splash.qml => Splash.qml} |   2 +
 electrum/gui/qml/components/Wallets.qml       |  32 +++++
 electrum/gui/qml/components/landing.qml       |  63 +++-----
 electrum/gui/qml/components/main.qml          |  55 ++++++-
 electrum/gui/qml/components/scan.qml          |  63 --------
 electrum/gui/qml/components/tx.qml            | 131 -----------------
 13 files changed, 388 insertions(+), 316 deletions(-)
 delete mode 100644 electrum/gui/qml/components/EButton.qml
 delete mode 100644 electrum/gui/qml/components/EHeader.qml
 create mode 100644 electrum/gui/qml/components/History.qml
 create mode 100644 electrum/gui/qml/components/NetworkStats.qml
 create mode 100644 electrum/gui/qml/components/QRScan.qml
 create mode 100644 electrum/gui/qml/components/Scan.qml
 create mode 100644 electrum/gui/qml/components/Send.qml
 rename electrum/gui/qml/components/{splash.qml => Splash.qml} (89%)
 create mode 100644 electrum/gui/qml/components/Wallets.qml
 delete mode 100644 electrum/gui/qml/components/scan.qml
 delete mode 100644 electrum/gui/qml/components/tx.qml

diff --git a/electrum/gui/qml/components/EButton.qml b/electrum/gui/qml/components/EButton.qml
deleted file mode 100644
index 87f7d459c..000000000
--- a/electrum/gui/qml/components/EButton.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-import QtQuick 2.6
-
-Item {
-    id: rootItem
-    width: visbut.width + 10
-    height: visbut.height + 10
-
-    signal clicked
-    property string text
-
-    Rectangle {
-        id: visbut
-        border {
-            color: '#444444'
-            width: 2
-        }
-        color: '#dddddd'
-        radius: 4
-
-        anchors.centerIn: parent
-        width: buttonText.width
-        height: buttonText.height
-
-        MouseArea {
-            anchors.fill: parent
-            onClicked: rootItem.clicked()
-        }
-    }
-
-    Text {
-        id: buttonText
-        leftPadding: 30
-        rightPadding: 30
-        topPadding: 20
-        bottomPadding: 20
-        verticalAlignment: Text.AlignVCenter
-        text: rootItem.text
-        color: 'red'
-    }
-
-}
diff --git a/electrum/gui/qml/components/EHeader.qml b/electrum/gui/qml/components/EHeader.qml
deleted file mode 100644
index 50283313f..000000000
--- a/electrum/gui/qml/components/EHeader.qml
+++ /dev/null
@@ -1,29 +0,0 @@
-import QtQuick 2.6
-
-Item {
-    height: 60
-
-    property alias text: label.text
-
-    Rectangle {
-        anchors.fill: parent
-        color: '#cccccc'
-    }
-
-    Text {
-        id: label
-        x: 10
-        anchors.verticalCenter: parent.verticalCenter
-        font.pointSize: 11
-        color: '#202020'
-    }
-
-    Rectangle {
-        x: 10
-        width: parent.width - 20
-        height: 2
-        anchors.topMargin: 0
-        anchors.top: label.bottom
-        color: '#808080'
-    }
-}
diff --git a/electrum/gui/qml/components/History.qml b/electrum/gui/qml/components/History.qml
new file mode 100644
index 000000000..cc7f898c6
--- /dev/null
+++ b/electrum/gui/qml/components/History.qml
@@ -0,0 +1,134 @@
+import QtQuick 2.6
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.0
+
+Item {
+    id: rootItem
+
+    property string title: 'History'
+
+    Column {
+        width: parent.width
+
+        ListView {
+            width: parent.width
+            height: 200
+
+            model: Daemon.currentWallet.historyModel
+            delegate: Item {
+                id: delegate
+                width: ListView.view.width
+                height: txinfo.height
+
+                MouseArea {
+                    anchors.fill: delegate
+                    onClicked: extinfo.visible = !extinfo.visible
+                }
+
+                GridLayout {
+                    id: txinfo
+                    columns: 4
+
+                    x: 6
+                    width: delegate.width - 12
+
+                    Item {
+                        id: indicator
+                        Layout.fillHeight: true
+                        Layout.rowSpan: 2
+                        Rectangle {
+                            width: 3
+                            color: model.incoming ? 'green' : 'red'
+                            y: 2
+                            height: parent.height - 4
+                        }
+                    }
+
+                    Image {
+                        readonly property variant tx_icons : [
+                            "../../../gui/icons/unconfirmed.png",
+                            "../../../gui/icons/clock1.png",
+                            "../../../gui/icons/clock2.png",
+                            "../../../gui/icons/clock3.png",
+                            "../../../gui/icons/clock4.png",
+                            "../../../gui/icons/clock5.png",
+                            "../../../gui/icons/confirmed.png"
+                        ]
+
+                        sourceSize.width: 32
+                        sourceSize.height: 32
+                        Layout.alignment: Qt.AlignVCenter
+                        source: tx_icons[Math.min(6,model.confirmations)]
+                    }
+
+                    Column {
+                        Layout.fillWidth: true
+
+                        Label {
+                            text: model.label !== '' ? model.label : '<no label>'
+                            color: model.label !== '' ? 'black' : 'gray'
+                            font.bold: model.label !== '' ? true : false
+                        }
+                        Label {
+                            font.pointSize: 7
+                            text: model.date
+                        }
+                    }
+
+                    Column {
+                        id: valuefee
+                        Label {
+                            text: model.bc_value
+                            font.bold: true
+                        }
+                        Label {
+                            font.pointSize: 6
+                            text: 'fee: ' + (model.fee !== undefined ? model.fee : '0')
+                        }
+                    }
+
+                    GridLayout {
+                        id: extinfo
+                        visible: false
+                        columns: 2
+                        Layout.columnSpan: 3
+
+                        Label { text: 'txid' }
+                        Label {
+                            font.pointSize: 6
+                            text: model.txid
+                            elide: Text.ElideMiddle
+                            Layout.fillWidth: true
+                        }
+                        Label { text: 'height' }
+                        Label {
+                            font.pointSize: 7
+                            text: model.height
+                        }
+                        Label { text: 'confirmations' }
+                        Label {
+                            font.pointSize: 7
+                            text: model.confirmations
+                        }
+                        Label { text: 'address' }
+                        Label {
+                            font.pointSize: 7
+                            elide: Text.ElideMiddle
+                            Layout.fillWidth: true
+                            text: {
+                                for (var i=0; i < Object.keys(model.outputs).length; i++) {
+                                    if (model.outputs[i].value === model.bc_value) {
+                                        return model.outputs[i].address
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                }
+            } // delegate
+        }
+
+    }
+
+}
diff --git a/electrum/gui/qml/components/NetworkStats.qml b/electrum/gui/qml/components/NetworkStats.qml
new file mode 100644
index 000000000..ef650cd1e
--- /dev/null
+++ b/electrum/gui/qml/components/NetworkStats.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.6
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.0
+import QtQuick.Controls.Material 2.0
+
+Item {
+    property string title: qsTr('Network')
+
+    GridLayout {
+        columns: 2
+
+        Label { text: qsTr("Server: "); color: Material.primaryHighlightedTextColor; font.bold: true }
+        Label { text: Network.server }
+        Label { text: qsTr("Local Height: "); color: Material.primaryHighlightedTextColor; font.bold: true }
+        Label { text: Network.height }
+        Label { text: qsTr("Status: "); color: Material.primaryHighlightedTextColor; font.bold: true }
+        Label { text: Network.status }
+        Label { text: qsTr("Wallet: "); color: Material.primaryHighlightedTextColor; font.bold: true }
+        Label { text: Daemon.walletName }
+    }
+}
diff --git a/electrum/gui/qml/components/QRScan.qml b/electrum/gui/qml/components/QRScan.qml
new file mode 100644
index 000000000..f04c4aa57
--- /dev/null
+++ b/electrum/gui/qml/components/QRScan.qml
@@ -0,0 +1,41 @@
+import QtQuick 2.6
+import QtMultimedia 5.6
+
+Item {
+
+    VideoOutput {
+        id: vo
+        anchors.fill: parent
+        source: camera
+        fillMode: VideoOutput.PreserveAspectCrop
+    }
+
+    MouseArea {
+        anchors.fill: parent
+        onClicked: {
+            vo.grabToImage(function(result) {
+                console.log("grab: image=" + (result.image !== undefined) + " url=" + result.url)
+                if (result.image !== undefined) {
+                    console.log('scanning image for QR')
+                    QR.scanImage(result.image)
+                }
+            })
+        }
+    }
+
+    Camera {
+        id: camera
+        deviceId: QtMultimedia.defaultCamera.deviceId
+        viewfinder.resolution: "640x480"
+
+        function dumpstats() {
+            console.log(camera.viewfinder.resolution)
+            console.log(camera.viewfinder.minimumFrameRate)
+            console.log(camera.viewfinder.maximumFrameRate)
+            var resolutions = camera.supportedViewfinderResolutions()
+            resolutions.forEach(function(item, i) {
+                console.log('' + item.width + 'x' + item.height)
+            })
+        }
+    }
+}
diff --git a/electrum/gui/qml/components/Scan.qml b/electrum/gui/qml/components/Scan.qml
new file mode 100644
index 000000000..61b8d179a
--- /dev/null
+++ b/electrum/gui/qml/components/Scan.qml
@@ -0,0 +1,23 @@
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+
+Item {
+
+    property bool toolbar: false
+    property string title: 'scan'
+
+    QRScan {
+        anchors.top: parent.top
+        anchors.bottom: button.top
+        width: parent.width
+    }
+
+    Button {
+        anchors.horizontalCenter: parent.horizontalCenter
+        id: button
+        anchors.bottom: parent.bottom
+        text: 'Cancel'
+        onClicked: app.stack.pop()
+    }
+
+}
diff --git a/electrum/gui/qml/components/Send.qml b/electrum/gui/qml/components/Send.qml
new file mode 100644
index 000000000..81e27890f
--- /dev/null
+++ b/electrum/gui/qml/components/Send.qml
@@ -0,0 +1,69 @@
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.0
+
+Item {
+    id: rootItem
+
+    property string title: 'Send'
+
+    GridLayout {
+        width: rootItem.width - 12
+        anchors.horizontalCenter: parent.horizontalCenter
+        columns: 4
+
+        Label {
+            Layout.columnSpan: 4
+            Layout.alignment: Qt.AlignHCenter
+            text: "Current Balance: 0 mBTC"
+        }
+
+        Label {
+            text: "Recipient"
+        }
+
+        TextField {
+            id: address
+            Layout.columnSpan: 3
+            placeholderText: 'Paste address or invoice'
+            Layout.fillWidth: true
+        }
+
+        Label {
+            text: "Amount"
+        }
+
+        TextField {
+            id: amount
+            placeholderText: 'Amount'
+        }
+
+        Label {
+            text: "Fee"
+        }
+
+        TextField {
+            id: fee
+            placeholderText: 'sat/vB'
+        }
+
+        Column {
+            Layout.fillWidth: true
+            Layout.columnSpan: 4
+
+            Button {
+                anchors.horizontalCenter: parent.horizontalCenter
+                text: 'Pay'
+                onClicked: {
+                    var i_amount = parseInt(amount.text)
+                    if (isNaN(i_amount))
+                        return
+                    var result = Daemon.currentWallet.send_onchain(address.text, i_amount, undefined, false)
+                    if (result)
+                        app.stack.pop()
+                }
+            }
+        }
+    }
+
+}
diff --git a/electrum/gui/qml/components/splash.qml b/electrum/gui/qml/components/Splash.qml
similarity index 89%
rename from electrum/gui/qml/components/splash.qml
rename to electrum/gui/qml/components/Splash.qml
index bb555f36a..8afc04f68 100644
--- a/electrum/gui/qml/components/splash.qml
+++ b/electrum/gui/qml/components/Splash.qml
@@ -1,6 +1,8 @@
 import QtQuick 2.0
 
 Item {
+    property bool toolbar: false
+
     Rectangle {
         anchors.fill: parent
         color: '#111144'
diff --git a/electrum/gui/qml/components/Wallets.qml b/electrum/gui/qml/components/Wallets.qml
new file mode 100644
index 000000000..5ce2fc859
--- /dev/null
+++ b/electrum/gui/qml/components/Wallets.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.6
+import QtQuick.Layouts 1.0
+import QtQuick.Controls 2.0
+
+Item {
+    property string title: 'Wallets'
+
+    ListView {
+        width: parent.width
+        height: 200
+        model: Daemon.activeWallets
+
+        delegate: Item {
+            width: ListView.view.width
+
+            RowLayout {
+                x: 20
+                spacing: 20
+
+                Image {
+                    source: "../../../gui/kivy/theming/light/wallet.png"
+                }
+
+                Label {
+                    font.pointSize: 13
+                    text: model.display
+                }
+            }
+        }
+    }
+
+}
diff --git a/electrum/gui/qml/components/landing.qml b/electrum/gui/qml/components/landing.qml
index 4a88d8170..73f3fcf5c 100644
--- a/electrum/gui/qml/components/landing.qml
+++ b/electrum/gui/qml/components/landing.qml
@@ -1,62 +1,31 @@
 import QtQuick 2.6
-import QtQuick.Controls 1.4
+import QtQuick.Controls 2.0
 import QtQml 2.6
 
 Item {
-    Column {
-        width: parent.width
+    property string title: 'Network'
 
-        EHeader {
-            text: "Network"
-            width: parent.width
-        }
+    property QtObject menu: Menu {
+        MenuItem { text: 'Wallets'; onTriggered: stack.push(Qt.resolvedUrl('Wallets.qml')) }
+        MenuItem { text: 'Network'; onTriggered: stack.push(Qt.resolvedUrl('NetworkStats.qml')) }
+    }
 
-        Row {
-            Text { text: "Server: " }
-            Text { text: Network.server }
-        }
-        Row {
-            Text { text: "Local Height: " }
-            Text { text: Network.height }
-        }
-        Row {
-            Text { text: "Status: " }
-            Text { text: Network.status }
-        }
-        Row {
-            Text { text: "Wallet: " }
-            Text { text: Daemon.walletName }
-        }
+    Column {
+        width: parent.width
 
-        EButton {
+        Button {
             text: 'Scan QR Code'
-            onClicked: app.stack.push(Qt.resolvedUrl('scan.qml'))
+            onClicked: app.stack.push(Qt.resolvedUrl('Scan.qml'))
         }
 
-        EButton {
-            text: 'Show TXen'
-            onClicked: app.stack.push(Qt.resolvedUrl('tx.qml'))
+        Button {
+            text: 'Send'
+            onClicked: app.stack.push(Qt.resolvedUrl('Send.qml'))
         }
 
-        ListView {
-            width: parent.width
-            height: 200
-            model: Daemon.activeWallets
-            delegate: Item {
-                width: parent.width
-
-                Row {
-                    Rectangle {
-                        width: 10
-                        height: parent.height
-                        color: 'red'
-                    }
-                    Text {
-                        leftPadding: 20
-                        text: model.display
-                    }
-                }
-            }
+        Button {
+            text: 'Show TX History'
+            onClicked: app.stack.push(Qt.resolvedUrl('History.qml'))
         }
 
     }
diff --git a/electrum/gui/qml/components/main.qml b/electrum/gui/qml/components/main.qml
index 233c671f3..bcbaeb002 100644
--- a/electrum/gui/qml/components/main.qml
+++ b/electrum/gui/qml/components/main.qml
@@ -1,5 +1,8 @@
 import QtQuick 2.6
-import QtQuick.Controls 1.4
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.0
+import QtQuick.Controls.Material 2.0
+
 import QtQml 2.6
 import QtMultimedia 5.6
 
@@ -9,22 +12,64 @@ ApplicationWindow
     visible: true
     width: 480
     height: 800
-    color: '#dddddd'
+
+    Material.theme: Material.Dark
+    Material.primary: Material.Indigo
+    Material.accent: Material.LightBlue
 
     property alias stack: mainStackView
 
+    header: ToolBar {
+        id: toolbar
+        RowLayout {
+            anchors.fill: parent
+            ToolButton {
+                text: qsTr("‹")
+                enabled: stack.currentItem.StackView.index > 0
+                onClicked: stack.pop()
+            }
+            Label {
+                text: stack.currentItem.title
+                elide: Label.ElideRight
+                horizontalAlignment: Qt.AlignHCenter
+                verticalAlignment: Qt.AlignVCenter
+                Layout.fillWidth: true
+            }
+            ToolButton {
+                text: qsTr("⋮")
+                onClicked: {
+                    stack.currentItem.menu.open()
+                    // position the menu to the right
+                    stack.currentItem.menu.x = toolbar.width - stack.currentItem.menu.width
+                }
+            }
+        }
+    }
+
     StackView {
         id: mainStackView
         anchors.fill: parent
 
-        initialItem: Qt.resolvedUrl('splash.qml')
+        initialItem: Qt.resolvedUrl('landing.qml')
     }
 
     Timer {
         id: splashTimer
-        interval: 400
+        interval: 1000
         onTriggered: {
-            mainStackView.push(Qt.resolvedUrl('landing.qml'))
+            splash.opacity = 0
+        }
+    }
+
+    Splash {
+        id: splash
+        anchors.top: header.top
+        anchors.bottom: app.contentItem.bottom
+        width: app.width
+        z: 1000
+
+        Behavior on opacity {
+            NumberAnimation { duration: 300 }
         }
     }
 
diff --git a/electrum/gui/qml/components/scan.qml b/electrum/gui/qml/components/scan.qml
deleted file mode 100644
index cec1e3710..000000000
--- a/electrum/gui/qml/components/scan.qml
+++ /dev/null
@@ -1,63 +0,0 @@
-import QtQuick 2.6
-import QtMultimedia 5.6
-
-
-Item {
-    Column {
-        width: parent.width
-
-        EHeader {
-            text: "Scan QR Code"
-            width: parent.width
-        }
-
-        Item {
-            id: voc
-            width: parent.width
-            height: parent.width
-
-            VideoOutput {
-                id: vo
-                anchors.fill: parent
-                source: camera
-                //fillMode: VideoOutput.PreserveAspectCrop
-            }
-
-            MouseArea {
-                anchors.fill: parent
-                onClicked: {
-                    vo.grabToImage(function(result) {
-                        console.log("grab: image=" + (result.image !== undefined) + " url=" + result.url)
-                        if (result.image !== undefined) {
-                            console.log('scanning image for QR')
-                            QR.scanImage(result.image)
-                        }
-                    })
-                }
-            }
-        }
-
-        EButton {
-            text: 'Exit'
-            onClicked: app.stack.pop()
-        }
-    }
-
-    Camera {
-        id: camera
-        deviceId: QtMultimedia.defaultCamera.deviceId
-        viewfinder.resolution: "640x480"
-
-        function dumpstats() {
-            console.log(camera.viewfinder.resolution)
-            console.log(camera.viewfinder.minimumFrameRate)
-            console.log(camera.viewfinder.maximumFrameRate)
-            var resolutions = camera.supportedViewfinderResolutions()
-            resolutions.forEach(function(item, i) {
-                console.log('' + item.width + 'x' + item.height)
-            })
-        }
-    }
-
-
-}
diff --git a/electrum/gui/qml/components/tx.qml b/electrum/gui/qml/components/tx.qml
deleted file mode 100644
index f8aefd0f1..000000000
--- a/electrum/gui/qml/components/tx.qml
+++ /dev/null
@@ -1,131 +0,0 @@
-import QtQuick 2.6
-
-Item {
-    id: rootItem
-
-    Column {
-        width: parent.width
-
-        EHeader {
-            text: "History"
-            width: parent.width
-        }
-
-        ListView {
-            width: parent.width
-            height: 200
-
-            model: Daemon.currentWallet.historyModel
-            delegate: Item {
-                id: delegate
-                width: parent.width
-                height: txinfo.height
-
-                MouseArea {
-                    anchors.fill: delegate
-                    onClicked: extinfo.visible = !extinfo.visible
-                }
-
-                Row {
-                    id: txinfo
-                    Rectangle {
-                        width: 4
-                        height: parent.height
-                        color: model.incoming ? 'green' : 'red'
-                    }
-
-                    Column {
-
-                        Row {
-                            id: baseinfo
-                            spacing: 10
-
-
-                            Image {
-                                readonly property variant tx_icons : [
-                                    "../../icons/unconfirmed.png",
-                                    "../../icons/clock1.png",
-                                    "../../icons/clock2.png",
-                                    "../../icons/clock3.png",
-                                    "../../icons/clock4.png",
-                                    "../../icons/clock5.png",
-                                    "../../icons/confirmed.png"
-                                ]
-
-                                width: 32
-                                height: 32
-                                anchors.verticalCenter: parent.verticalCenter
-                                source: tx_icons[Math.min(6,Math.floor(model.confirmations/20))]
-                            }
-
-                            Column {
-                                id: content
-                                width: delegate.width - x - valuefee.width
-
-                                Text {
-                                    text: model.label !== '' ? model.label : '<no label>'
-                                    color: model.label !== '' ? 'black' : 'gray'
-                                }
-                                Text {
-                                    font.pointSize: 7
-                                    text: model.date
-                                }
-                            }
-
-                            Column {
-                                id: valuefee
-                                width: delegate.width * 0.25
-                                Text {
-                                    text: model.bc_value
-                                }
-                                Text {
-                                    font.pointSize: 7
-                                    text: 'fee: ' + (model.fee !== undefined ? model.fee : '0')
-                                }
-                            }
-                        }
-
-                        Row {
-                            id: extinfo
-                            visible: false
-
-                            Column {
-                                id: extinfoinner
-                                Text {
-                                    font.pointSize: 6
-                                    text: 'txid: ' + model.txid
-                                }
-                                Text {
-                                    font.pointSize: 7
-                                    text: 'height: ' + model.height
-                                }
-                                Text {
-                                    font.pointSize: 7
-                                    text: 'confirmations: ' + model.confirmations
-                                }
-                                Text {
-                                    font.pointSize: 7
-                                    text: {
-                                        for (var i=0; i < Object.keys(model.outputs).length; i++) {
-                                            if (model.outputs[i].value === model.bc_value) {
-                                                return 'address: ' + model.outputs[i].address
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-
-
-                    }
-                }
-            } // delegate
-        }
-
-        EButton {
-            text: 'Back'
-            onClicked: app.stack.pop()
-        }
-    }
-
-}