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() - } - } - -}