9 changed files with 365 additions and 4 deletions
@ -0,0 +1,211 @@ |
|||||||
|
import QtQuick 2.15 |
||||||
|
import QtQuick.Layouts 1.0 |
||||||
|
import QtQuick.Controls 2.14 |
||||||
|
import QtQuick.Controls.Material 2.0 |
||||||
|
|
||||||
|
import org.electrum 1.0 |
||||||
|
|
||||||
|
import "controls" |
||||||
|
|
||||||
|
ElDialog { |
||||||
|
id: dialog |
||||||
|
|
||||||
|
enum Check { |
||||||
|
Unknown, |
||||||
|
Valid, |
||||||
|
Invalid |
||||||
|
} |
||||||
|
|
||||||
|
property string address |
||||||
|
|
||||||
|
property bool _addressValid: false |
||||||
|
property bool _addressMine: false |
||||||
|
property int _verified: SignVerifyMessageDialog.Check.Unknown |
||||||
|
|
||||||
|
implicitHeight: parent.height |
||||||
|
implicitWidth: parent.width |
||||||
|
|
||||||
|
title: qsTr('Sign/Verify Message') |
||||||
|
iconSource: Qt.resolvedUrl('../../icons/pen.png') |
||||||
|
|
||||||
|
padding: 0 |
||||||
|
|
||||||
|
function validateAddress() { |
||||||
|
// TODO: not all types of addresses are valid (e.g. p2wsh) |
||||||
|
_addressValid = bitcoin.isAddress(addressField.text) |
||||||
|
_addressMine = Daemon.currentWallet.isAddressMine(addressField.text) |
||||||
|
} |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
width: parent.width |
||||||
|
height: parent.height |
||||||
|
spacing: constants.paddingLarge |
||||||
|
|
||||||
|
ColumnLayout { |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.fillHeight: true |
||||||
|
Layout.leftMargin: constants.paddingLarge |
||||||
|
Layout.rightMargin: constants.paddingLarge |
||||||
|
|
||||||
|
Label { |
||||||
|
text: qsTr('Address') |
||||||
|
color: Material.accentColor |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
Layout.fillWidth: true |
||||||
|
TextField { |
||||||
|
id: addressField |
||||||
|
Layout.fillWidth: true |
||||||
|
placeholderText: qsTr('Address') |
||||||
|
font.family: FixedFont |
||||||
|
onTextChanged: { |
||||||
|
validateAddress() |
||||||
|
_verified = SignVerifyMessageDialog.Check.Unknown |
||||||
|
} |
||||||
|
} |
||||||
|
ToolButton { |
||||||
|
icon.source: '../../icons/paste.png' |
||||||
|
icon.color: 'transparent' |
||||||
|
onClicked: { |
||||||
|
addressField.text = AppController.clipboardToText() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Label { |
||||||
|
text: qsTr('Message') |
||||||
|
color: Material.accentColor |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.fillHeight: true |
||||||
|
ElTextArea { |
||||||
|
id: plaintext |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.fillHeight: true |
||||||
|
font.family: FixedFont |
||||||
|
wrapMode: TextInput.Wrap |
||||||
|
background: PaneInsetBackground { |
||||||
|
baseColor: constants.darkerDialogBackground |
||||||
|
} |
||||||
|
onTextChanged: _verified = SignVerifyMessageDialog.Check.Unknown |
||||||
|
} |
||||||
|
ColumnLayout { |
||||||
|
Layout.alignment: Qt.AlignTop |
||||||
|
ToolButton { |
||||||
|
icon.source: '../../icons/paste.png' |
||||||
|
icon.color: 'transparent' |
||||||
|
onClicked: { |
||||||
|
plaintext.text = AppController.clipboardToText() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
RowLayout { |
||||||
|
Layout.fillWidth: true |
||||||
|
Label { |
||||||
|
text: qsTr('Signature') |
||||||
|
color: Material.accentColor |
||||||
|
} |
||||||
|
Label { |
||||||
|
Layout.alignment: Qt.AlignRight |
||||||
|
visible: _verified != SignVerifyMessageDialog.Check.Unknown |
||||||
|
text: _verified == SignVerifyMessageDialog.Check.Valid |
||||||
|
? qsTr('Valid!') |
||||||
|
: qsTr('Invalid!') |
||||||
|
color: _verified == SignVerifyMessageDialog.Check.Valid |
||||||
|
? constants.colorDone |
||||||
|
: constants.colorError |
||||||
|
} |
||||||
|
} |
||||||
|
RowLayout { |
||||||
|
Layout.fillWidth: true |
||||||
|
ElTextArea { |
||||||
|
id: signature |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.maximumHeight: fontMetrics.lineSpacing * 4 + topPadding + bottomPadding |
||||||
|
Layout.minimumHeight: fontMetrics.lineSpacing * 4 + topPadding + bottomPadding |
||||||
|
font.family: FixedFont |
||||||
|
wrapMode: TextInput.Wrap |
||||||
|
background: PaneInsetBackground { |
||||||
|
baseColor: _verified == SignVerifyMessageDialog.Check.Unknown |
||||||
|
? constants.darkerDialogBackground |
||||||
|
: _verified == SignVerifyMessageDialog.Check.Valid |
||||||
|
? constants.colorValidBackground |
||||||
|
: constants.colorInvalidBackground |
||||||
|
} |
||||||
|
onTextChanged: _verified = SignVerifyMessageDialog.Check.Unknown |
||||||
|
} |
||||||
|
ColumnLayout { |
||||||
|
Layout.alignment: Qt.AlignTop |
||||||
|
ToolButton { |
||||||
|
icon.source: '../../icons/paste.png' |
||||||
|
icon.color: 'transparent' |
||||||
|
onClicked: { |
||||||
|
signature.text = AppController.clipboardToText() |
||||||
|
} |
||||||
|
} |
||||||
|
ToolButton { |
||||||
|
icon.source: '../../icons/share.png' |
||||||
|
icon.color: enabled ? 'transparent' : Material.iconDisabledColor |
||||||
|
enabled: signature.text |
||||||
|
onClicked: { |
||||||
|
var dialog = app.genericShareDialog.createObject(app, { |
||||||
|
title: qsTr('Message signature'), |
||||||
|
text_qr: signature.text |
||||||
|
}) |
||||||
|
dialog.open() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ButtonContainer { |
||||||
|
Layout.fillWidth: true |
||||||
|
FlatButton { |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.preferredWidth: 1 |
||||||
|
text: qsTr('Sign') |
||||||
|
visible: Daemon.currentWallet.canSignMessage |
||||||
|
enabled: _addressMine |
||||||
|
icon.source: '../../icons/seal.png' |
||||||
|
onClicked: { |
||||||
|
var sig = Daemon.currentWallet.signMessage(addressField.text, plaintext.text) |
||||||
|
signature.text = sig |
||||||
|
} |
||||||
|
} |
||||||
|
FlatButton { |
||||||
|
Layout.fillWidth: true |
||||||
|
Layout.preferredWidth: 1 |
||||||
|
enabled: _addressValid && signature.text |
||||||
|
text: qsTr('Verify') |
||||||
|
icon.source: '../../icons/confirmed.png' |
||||||
|
onClicked: { |
||||||
|
var result = Daemon.verifyMessage(addressField.text, plaintext.text, signature.text) |
||||||
|
_verified = result |
||||||
|
? SignVerifyMessageDialog.Check.Valid |
||||||
|
: SignVerifyMessageDialog.Check.Invalid |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Component.onCompleted: { |
||||||
|
addressField.text = address |
||||||
|
} |
||||||
|
|
||||||
|
Bitcoin { |
||||||
|
id: bitcoin |
||||||
|
} |
||||||
|
|
||||||
|
FontMetrics { |
||||||
|
id: fontMetrics |
||||||
|
font: signature.font |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
import QtQuick 2.15 |
||||||
|
import QtQuick.Layouts 1.0 |
||||||
|
import QtQuick.Controls 2.14 |
||||||
|
import QtQuick.Controls.Material 2.0 |
||||||
|
|
||||||
|
import org.electrum 1.0 |
||||||
|
|
||||||
|
// this component adds (auto)scrolling to the bare TextArea, to make it |
||||||
|
// workable if text overflows the available space. |
||||||
|
// This unfortunately hides many signals and properties from the TextArea, |
||||||
|
// so add signals propagation and property aliases when needed. |
||||||
|
Flickable { |
||||||
|
id: root |
||||||
|
|
||||||
|
property alias text: edit.text |
||||||
|
property alias wrapMode: edit.wrapMode |
||||||
|
property alias background: rootpane.background |
||||||
|
property alias font: edit.font |
||||||
|
|
||||||
|
contentWidth: rootpane.width |
||||||
|
contentHeight: rootpane.height |
||||||
|
clip: true |
||||||
|
|
||||||
|
boundsBehavior: Flickable.StopAtBounds |
||||||
|
flickableDirection: Flickable.VerticalFlick |
||||||
|
|
||||||
|
function ensureVisible(r) { |
||||||
|
r.x = r.x + rootpane.leftPadding |
||||||
|
r.y = r.y + rootpane.topPadding |
||||||
|
var w = width - rootpane.leftPadding - rootpane.rightPadding |
||||||
|
var h = height - rootpane.topPadding - rootpane.bottomPadding |
||||||
|
if (contentX >= r.x) |
||||||
|
contentX = r.x |
||||||
|
else if (contentX+w <= r.x+r.width) |
||||||
|
contentX = r.x+r.width-w |
||||||
|
if (contentY >= r.y) |
||||||
|
contentY = r.y |
||||||
|
else if (contentY+h <= r.y+r.height) |
||||||
|
contentY = r.y+r.height-h |
||||||
|
} |
||||||
|
|
||||||
|
Pane { |
||||||
|
id: rootpane |
||||||
|
width: root.width |
||||||
|
height: Math.max(root.height, edit.height + topPadding + bottomPadding) |
||||||
|
padding: constants.paddingXSmall |
||||||
|
TextArea { |
||||||
|
id: edit |
||||||
|
width: parent.width |
||||||
|
focus: true |
||||||
|
wrapMode: TextEdit.Wrap |
||||||
|
onCursorRectangleChanged: root.ensureVisible(cursorRectangle) |
||||||
|
onTextChanged: root.textChanged() |
||||||
|
background: Rectangle { |
||||||
|
color: 'transparent' |
||||||
|
} |
||||||
|
} |
||||||
|
MouseArea { |
||||||
|
// remaining area clicks focus textarea |
||||||
|
width: parent.width |
||||||
|
anchors.top: edit.bottom |
||||||
|
anchors.bottom: parent.bottom |
||||||
|
onClicked: edit.forceActiveFocus() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue