Browse Source

Merge branch 'seed_keyboard'

master
Sander van Grieken 3 years ago
parent
commit
1a889d19b5
  1. 4
      electrum/gui/qml/__init__.py
  2. 93
      electrum/gui/qml/components/controls/SeedKeyboard.qml
  3. 43
      electrum/gui/qml/components/controls/SeedKeyboardKey.qml
  4. 92
      electrum/gui/qml/components/controls/SeedTextArea.qml
  5. 54
      electrum/gui/qml/components/main.qml
  6. 2
      electrum/gui/qml/components/wizard/WCConfirmSeed.qml
  7. 23
      electrum/gui/qml/components/wizard/WCHaveSeed.qml

4
electrum/gui/qml/__init__.py

@ -61,10 +61,6 @@ class ElectrumGui(BaseElectrumGui, Logger):
# os.environ['QML_IMPORT_TRACE'] = '1' # os.environ['QML_IMPORT_TRACE'] = '1'
# os.environ['QT_DEBUG_PLUGINS'] = '1' # os.environ['QT_DEBUG_PLUGINS'] = '1'
os.environ['QT_IM_MODULE'] = 'qtvirtualkeyboard'
os.environ['QT_VIRTUALKEYBOARD_STYLE'] = 'Electrum'
os.environ['QML2_IMPORT_PATH'] = 'electrum/gui/qml'
os.environ['QT_ANDROID_DISABLE_ACCESSIBILITY'] = '1' os.environ['QT_ANDROID_DISABLE_ACCESSIBILITY'] = '1'
# set default locale to en_GB. This is for l10n (e.g. number formatting, number input etc), # set default locale to en_GB. This is for l10n (e.g. number formatting, number input etc),

93
electrum/gui/qml/components/controls/SeedKeyboard.qml

@ -0,0 +1,93 @@
import QtQuick 2.15
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.0
Item {
id: root
signal keyEvent(keycode: int, text: string)
property int hpadding: 0
property int vpadding: 15
property int keywidth: (root.width - 2 * hpadding) / 10 - keyhspacing
property int keyheight: (root.height - 2 * vpadding) / 4 - keyvspacing
property int keyhspacing: 4
property int keyvspacing: 5
function emitKeyEvent(key, keycode) {
keyEvent(keycode, key)
}
ColumnLayout {
id: rootLayout
x: hpadding
y: vpadding
width: parent.width - 2*hpadding
spacing: keyvspacing
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: keyhspacing
Repeater {
model: ['q','w','e','r','t','y','u','i','o','p']
delegate: SeedKeyboardKey {
key: modelData
kbd: root
implicitWidth: keywidth
implicitHeight: keyheight
}
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: keyhspacing
Repeater {
model: ['a','s','d','f','g','h','j','k','l']
delegate: SeedKeyboardKey {
key: modelData
kbd: root
implicitWidth: keywidth
implicitHeight: keyheight
}
}
// spacer
Item { Layout.preferredHeight: 1; Layout.preferredWidth: keywidth / 2 }
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: keyhspacing
Repeater {
model: ['z','x','c','v','b','n','m']
delegate: SeedKeyboardKey {
key: modelData
kbd: root
implicitWidth: keywidth
implicitHeight: keyheight
}
}
// spacer
Item { Layout.preferredHeight: 1; Layout.preferredWidth: keywidth }
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
SeedKeyboardKey {
key: ' '
keycode: Qt.Key_Space
kbd: root
implicitWidth: keywidth * 5
implicitHeight: keyheight
}
SeedKeyboardKey {
key: '<'
keycode: Qt.Key_Backspace
kbd: root
implicitWidth: keywidth
implicitHeight: keyheight
}
// spacer
Item { Layout.preferredHeight: 1; Layout.preferredWidth: keywidth / 2 }
}
}
}

43
electrum/gui/qml/components/controls/SeedKeyboardKey.qml

@ -0,0 +1,43 @@
import QtQuick 2.15
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.0
Pane {
id: root
property string key
property int keycode: -1
property QtObject kbd
padding: 1
function emitKeyEvent() {
if (keycode == -1) {
keycode = parseInt(key, 36) - 9 + 0x40 // map a-z char to key code
}
kbd.keyEvent(keycode, key)
}
FlatButton {
anchors.fill: parent
focusPolicy: Qt.NoFocus
autoRepeat: true
autoRepeatDelay: 750
text: key
padding: 0
font.pixelSize: Math.max(root.height * 1/3, constants.fontSizeSmall)
onClicked: {
emitKeyEvent()
}
// send keyevent again, otherwise it is ignored
onDoubleClicked: {
emitKeyEvent()
}
}
}

92
electrum/gui/qml/components/controls/SeedTextArea.qml

@ -11,8 +11,10 @@ Pane {
padding: 0 padding: 0
property string text property string text
property alias readOnly: seedtextarea.readOnly property bool readOnly: false
property alias placeholderText: seedtextarea.placeholderText property alias placeholderText: seedtextarea.placeholderText
property string indicatorText
property bool indicatorValid
property var _suggestions: [] property var _suggestions: []
@ -29,6 +31,56 @@ Pane {
id: rootLayout id: rootLayout
width: parent.width width: parent.width
spacing: 0 spacing: 0
TextArea {
id: seedtextarea
Layout.fillWidth: true
Layout.minimumHeight: fontMetrics.height * 3 + topPadding + bottomPadding
rightPadding: constants.paddingLarge
leftPadding: constants.paddingLarge
wrapMode: TextInput.WordWrap
font.bold: true
font.pixelSize: constants.fontSizeLarge
font.family: FixedFont
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhLowercaseOnly | Qt.ImhNoPredictiveText
readOnly: true
background: Rectangle {
color: constants.darkerBackground
}
onTextChanged: {
// work around Qt issue, TextArea fires spurious textChanged events
// NOTE: might be Qt virtual keyboard, or Qt upgrade from 5.15.2 to 5.15.7
if (root.text != text)
root.text = text
// update suggestions
_suggestions = bitcoin.mnemonicsFor(seedtextarea.text.split(' ').pop())
// TODO: cursorPosition only on suggestion apply
cursorPosition = text.length
}
Rectangle {
anchors.fill: contentText
color: root.indicatorValid ? 'green' : 'red'
border.color: Material.accentColor
radius: 2
}
Label {
id: contentText
text: root.indicatorText
anchors.right: parent.right
anchors.bottom: parent.bottom
leftPadding: root.indicatorText != '' ? constants.paddingLarge : 0
rightPadding: root.indicatorText != '' ? constants.paddingLarge : 0
font.bold: false
font.pixelSize: constants.fontSizeSmall
}
}
Flickable { Flickable {
Layout.preferredWidth: parent.width Layout.preferredWidth: parent.width
Layout.minimumHeight: fontMetrics.lineSpacing + 2*constants.paddingXXSmall + 2*constants.paddingXSmall + 2 Layout.minimumHeight: fontMetrics.lineSpacing + 2*constants.paddingXXSmall + 2*constants.paddingXSmall + 2
@ -70,34 +122,18 @@ Pane {
} }
} }
TextArea { SeedKeyboard {
id: seedtextarea id: kbd
Layout.fillWidth: true Layout.fillWidth: true
Layout.minimumHeight: fontMetrics.height * 3 + topPadding + bottomPadding Layout.preferredHeight: kbd.width / 2
visible: !root.readOnly
rightPadding: constants.paddingLarge onKeyEvent: {
leftPadding: constants.paddingLarge if (keycode == Qt.Key_Backspace) {
if (seedtextarea.text.length > 0)
wrapMode: TextInput.WordWrap seedtextarea.text = seedtextarea.text.substring(0, seedtextarea.text.length-1)
font.bold: true } else {
font.pixelSize: constants.fontSizeLarge seedtextarea.text = seedtextarea.text + text
font.family: FixedFont }
inputMethodHints: Qt.ImhSensitiveData | Qt.ImhLowercaseOnly | Qt.ImhNoPredictiveText
background: Rectangle {
color: constants.darkerBackground
}
onTextChanged: {
// work around Qt issue, TextArea fires spurious textChanged events
// NOTE: might be Qt virtual keyboard, or Qt upgrade from 5.15.2 to 5.15.7
if (root.text != text)
root.text = text
// update suggestions
_suggestions = bitcoin.mnemonicsFor(seedtextarea.text.split(' ').pop())
// TODO: cursorPosition only on suggestion apply
cursorPosition = text.length
} }
} }
} }

54
electrum/gui/qml/components/main.qml

@ -3,6 +3,7 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0 import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Material.impl 2.12 import QtQuick.Controls.Material.impl 2.12
import QtQuick.Window 2.15
import QtQml 2.6 import QtQml 2.6
import QtMultimedia 5.6 import QtMultimedia 5.6
@ -31,7 +32,6 @@ ApplicationWindow
Constants { id: appconstants } Constants { id: appconstants }
property alias stack: mainStackView property alias stack: mainStackView
property alias inputPanel: inputPanel
property variant activeDialogs: [] property variant activeDialogs: []
@ -224,7 +224,7 @@ ApplicationWindow
StackView { StackView {
id: mainStackView id: mainStackView
width: parent.width width: parent.width
height: inputPanel.y - header.height height: keyboardFreeZone.height - header.height
initialItem: Qt.resolvedUrl('WalletMainView.qml') initialItem: Qt.resolvedUrl('WalletMainView.qml')
function getRoot() { function getRoot() {
@ -266,39 +266,47 @@ ApplicationWindow
} }
Item { Item {
id: keyboardFreeZone
// Item as first child in Overlay that adjusts its size to the available // Item as first child in Overlay that adjusts its size to the available
// screen space minus the virtual keyboard (e.g. to center dialogs in) // screen space minus the virtual keyboard (e.g. to center dialogs in)
// see ElDialog.resizeWithKeyboard property // see also ElDialog.resizeWithKeyboard property
parent: Overlay.overlay parent: Overlay.overlay
width: parent.width width: parent.width
height: inputPanel.y height: parent.height
}
InputPanel {
id: inputPanel
width: parent.width
y: parent.height
states: State { states: State {
name: "visible" name: "visible"
when: inputPanel.active when: Qt.inputMethod.visible
PropertyChanges { PropertyChanges {
target: inputPanel target: keyboardFreeZone
y: parent.height - height height: keyboardFreeZone.parent.height - Qt.inputMethod.keyboardRectangle.height / Screen.devicePixelRatio
} }
} }
transitions: Transition { transitions: [
from: '' Transition {
to: 'visible' from: ''
reversible: true to: 'visible'
ParallelAnimation { ParallelAnimation {
NumberAnimation { NumberAnimation {
properties: "y" properties: "height"
duration: 250 duration: 250
easing.type: Easing.OutQuad easing.type: Easing.OutQuad
}
}
},
Transition {
from: 'visible'
to: ''
ParallelAnimation {
NumberAnimation {
properties: "height"
duration: 50
easing.type: Easing.OutQuad
}
} }
} }
} ]
} }
property alias newWalletWizard: _newWalletWizard property alias newWalletWizard: _newWalletWizard

2
electrum/gui/qml/components/wizard/WCConfirmSeed.qml

@ -30,13 +30,13 @@ WizardComponent {
InfoTextArea { InfoTextArea {
Layout.fillWidth: true Layout.fillWidth: true
Layout.bottomMargin: constants.paddingLarge
text: qsTr('Your seed is important!') + ' ' + text: qsTr('Your seed is important!') + ' ' +
qsTr('If you lose your seed, your money will be permanently lost.') + ' ' + qsTr('If you lose your seed, your money will be permanently lost.') + ' ' +
qsTr('To make sure that you have properly saved your seed, please retype it here.') qsTr('To make sure that you have properly saved your seed, please retype it here.')
} }
Label { Label {
Layout.topMargin: constants.paddingMedium
text: qsTr('Confirm your seed (re-enter)') text: qsTr('Confirm your seed (re-enter)')
} }

23
electrum/gui/qml/components/wizard/WCHaveSeed.qml

@ -164,6 +164,7 @@ WizardComponent {
id: infotext id: infotext
Layout.fillWidth: true Layout.fillWidth: true
Layout.columnSpan: 2 Layout.columnSpan: 2
Layout.bottomMargin: constants.paddingLarge
} }
SeedTextArea { SeedTextArea {
@ -173,25 +174,11 @@ WizardComponent {
placeholderText: cosigner ? qsTr('Enter cosigner seed') : qsTr('Enter your seed') placeholderText: cosigner ? qsTr('Enter cosigner seed') : qsTr('Enter your seed')
indicatorValid: root.valid
onTextChanged: { onTextChanged: {
startValidationTimer() startValidationTimer()
} }
Rectangle {
anchors.fill: contentText
color: root.valid ? 'green' : 'red'
border.color: Material.accentColor
radius: 2
}
Label {
id: contentText
anchors.right: parent.right
anchors.bottom: parent.bottom
leftPadding: text != '' ? constants.paddingLarge : 0
rightPadding: text != '' ? constants.paddingLarge : 0
font.bold: false
font.pixelSize: constants.fontSizeSmall
}
} }
TextArea { TextArea {
id: validationtext id: validationtext
@ -223,13 +210,13 @@ WizardComponent {
Bitcoin { Bitcoin {
id: bitcoin id: bitcoin
onSeedTypeChanged: contentText.text = bitcoin.seedType onSeedTypeChanged: seedtext.indicatorText = bitcoin.seedType
onValidationMessageChanged: validationtext.text = validationMessage onValidationMessageChanged: validationtext.text = validationMessage
} }
function startValidationTimer() { function startValidationTimer() {
valid = false valid = false
contentText.text = '' seedtext.indicatorText = ''
validationTimer.restart() validationTimer.restart()
} }

Loading…
Cancel
Save