kivy wizard: use own soft keyboard
This commit is contained in:
parent
950f3ae633
commit
656069070a
|
@ -230,11 +230,11 @@ class ElectrumWindow(App):
|
||||||
|
|
||||||
def set_URI(self, url):
|
def set_URI(self, url):
|
||||||
try:
|
try:
|
||||||
url = electrum.util.parse_URI(url, self.on_pr)
|
d = electrum.util.parse_URI(url, self.on_pr)
|
||||||
except:
|
except:
|
||||||
self.show_info(_("Not a Bitcoin URI") + ':\n', url)
|
self.show_info(_("Not a Bitcoin URI") + ':\n', url)
|
||||||
return
|
return
|
||||||
self.send_screen.set_URI(url)
|
self.send_screen.set_URI(d)
|
||||||
|
|
||||||
def on_qr(self, data):
|
def on_qr(self, data):
|
||||||
if data.startswith('bitcoin:'):
|
if data.startswith('bitcoin:'):
|
||||||
|
|
|
@ -11,6 +11,7 @@ from kivy.clock import Clock
|
||||||
from kivy.lang import Builder
|
from kivy.lang import Builder
|
||||||
from kivy.properties import ObjectProperty, StringProperty, OptionProperty
|
from kivy.properties import ObjectProperty, StringProperty, OptionProperty
|
||||||
from kivy.core.window import Window
|
from kivy.core.window import Window
|
||||||
|
from kivy.uix.button import Button
|
||||||
|
|
||||||
from electrum_gui.kivy.uix.dialogs import EventsDialog
|
from electrum_gui.kivy.uix.dialogs import EventsDialog
|
||||||
from electrum_gui.kivy.i18n import _
|
from electrum_gui.kivy.i18n import _
|
||||||
|
@ -60,62 +61,49 @@ Builder.load_string('''
|
||||||
BoxLayout:
|
BoxLayout:
|
||||||
orientation: 'vertical' if self.width < self.height else 'horizontal'
|
orientation: 'vertical' if self.width < self.height else 'horizontal'
|
||||||
padding:
|
padding:
|
||||||
min(dp(42), self.width/8), min(dp(60), self.height/9.7),\
|
min(dp(42), self.width/16), min(dp(60), self.height/16),\
|
||||||
min(dp(42), self.width/8), min(dp(72), self.height/8)
|
min(dp(42), self.width/16), min(dp(72), self.height/16)
|
||||||
spacing: '27dp'
|
spacing: '27dp'
|
||||||
GridLayout:
|
GridLayout:
|
||||||
id: grid_logo
|
id: grid_logo
|
||||||
cols: 1
|
cols: 1
|
||||||
pos_hint: {'center_y': .5}
|
pos_hint: {'center_y': .5}
|
||||||
size_hint: 1, .42
|
size_hint: 1, None
|
||||||
#height: self.minimum_height
|
#height: self.minimum_height
|
||||||
Image:
|
|
||||||
id: logo_img
|
|
||||||
mipmap: True
|
|
||||||
allow_stretch: True
|
|
||||||
size_hint: 1, None
|
|
||||||
height: '110dp'
|
|
||||||
source: 'atlas://gui/kivy/theming/light/electrum_icon640'
|
|
||||||
Widget:
|
|
||||||
size_hint: 1, None
|
|
||||||
height: 0 if stepper.opacity else dp(15)
|
|
||||||
Label:
|
Label:
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
opacity: 0 if stepper.opacity else 1
|
|
||||||
text: 'ELECTRUM'
|
text: 'ELECTRUM'
|
||||||
size_hint: 1, None
|
size_hint: 1, None
|
||||||
height: self.texture_size[1] if self.opacity else 0
|
height: self.texture_size[1] if self.opacity else 0
|
||||||
font_size: '33sp'
|
font_size: '33sp'
|
||||||
font_name: 'gui/kivy/data/fonts/tron/Tr2n.ttf'
|
font_name: 'gui/kivy/data/fonts/tron/Tr2n.ttf'
|
||||||
Image:
|
|
||||||
id: stepper
|
|
||||||
allow_stretch: True
|
|
||||||
opacity: 0
|
|
||||||
source: 'atlas://gui/kivy/theming/light/stepper_left'
|
|
||||||
size_hint: 1, None
|
|
||||||
height: grid_logo.height/2.5 if self.opacity else 0
|
|
||||||
Widget:
|
|
||||||
size_hint: None, None
|
|
||||||
size: '5dp', '5dp'
|
|
||||||
GridLayout:
|
GridLayout:
|
||||||
cols: 1
|
cols: 1
|
||||||
id: crcontent
|
id: crcontent
|
||||||
spacing: '13dp'
|
spacing: '1dp'
|
||||||
|
|
||||||
|
|
||||||
<CreateRestoreDialog>
|
<CreateRestoreDialog>
|
||||||
|
Image:
|
||||||
|
id: logo_img
|
||||||
|
mipmap: True
|
||||||
|
allow_stretch: True
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '110dp'
|
||||||
|
source: 'atlas://gui/kivy/theming/light/electrum_icon640'
|
||||||
|
Widget:
|
||||||
|
size_hint: 1, 1
|
||||||
Label:
|
Label:
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
size_hint: 1, None
|
size_hint: 1, None
|
||||||
text_size: self.width, None
|
text_size: self.width, None
|
||||||
height: self.texture_size[1]
|
height: self.texture_size[1]
|
||||||
text:
|
text:
|
||||||
_("Wallet file not found!!")+"\\n\\n" +\
|
_("Wallet file not found")+"\\n\\n" +\
|
||||||
_("Do you want to create a new wallet ")+\
|
_("Do you want to create a new wallet ")+\
|
||||||
_("or restore an existing one?")
|
_("or restore an existing one?")
|
||||||
Widget
|
Widget
|
||||||
size_hint: 1, None
|
size_hint: 1, 1
|
||||||
height: dp(15)
|
|
||||||
GridLayout:
|
GridLayout:
|
||||||
id: grid
|
id: grid
|
||||||
orientation: 'vertical'
|
orientation: 'vertical'
|
||||||
|
@ -133,7 +121,23 @@ Builder.load_string('''
|
||||||
root: root
|
root: root
|
||||||
|
|
||||||
|
|
||||||
|
<MButton@Button>:
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '33dp'
|
||||||
|
on_release:
|
||||||
|
self.parent.update_amount(self.text)
|
||||||
|
|
||||||
|
<WordButton@Button>:
|
||||||
|
size_hint: None, None
|
||||||
|
text_size: None, self.height
|
||||||
|
width: self.texture_size[0]
|
||||||
|
height: '30dp'
|
||||||
|
on_release:
|
||||||
|
self.parent.new_word(self.text)
|
||||||
|
|
||||||
|
|
||||||
<RestoreSeedDialog>
|
<RestoreSeedDialog>
|
||||||
|
word: ''
|
||||||
Label:
|
Label:
|
||||||
color: root.text_color
|
color: root.text_color
|
||||||
size_hint: 1, None
|
size_hint: 1, None
|
||||||
|
@ -147,13 +151,20 @@ Builder.load_string('''
|
||||||
spacing: '12dp'
|
spacing: '12dp'
|
||||||
size_hint: 1, None
|
size_hint: 1, None
|
||||||
height: self.minimum_height
|
height: self.minimum_height
|
||||||
WizardTextInput:
|
Button:
|
||||||
|
border: 4, 4, 4, 4
|
||||||
|
halign: 'justify'
|
||||||
|
valign: 'middle'
|
||||||
|
font_size: self.width/15
|
||||||
|
text_size: self.width - dp(24), self.height - dp(12)
|
||||||
|
color: .1, .1, .1, 1
|
||||||
|
background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top'
|
||||||
|
background_down: self.background_normal
|
||||||
id: text_input_seed
|
id: text_input_seed
|
||||||
size_hint: 1, None
|
size_hint_y: None
|
||||||
height: '110dp'
|
height: dp(100)
|
||||||
hint_text:
|
text: ''
|
||||||
_('Enter your seedphrase')
|
on_text: Clock.schedule_once(root.on_text)
|
||||||
on_text: root._trigger_check_seed()
|
|
||||||
Label:
|
Label:
|
||||||
font_size: '12sp'
|
font_size: '12sp'
|
||||||
text_size: self.width, None
|
text_size: self.width, None
|
||||||
|
@ -165,6 +176,80 @@ Builder.load_string('''
|
||||||
on_ref_press:
|
on_ref_press:
|
||||||
import webbrowser
|
import webbrowser
|
||||||
webbrowser.open('https://electrum.org/faq.html#seed')
|
webbrowser.open('https://electrum.org/faq.html#seed')
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
id: suggestions
|
||||||
|
height: '35dp'
|
||||||
|
size_hint: 1, None
|
||||||
|
new_word: root.on_word
|
||||||
|
|
||||||
|
BoxLayout:
|
||||||
|
update_amount: root.update_text
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '30dp'
|
||||||
|
MButton:
|
||||||
|
text: 'Q'
|
||||||
|
MButton:
|
||||||
|
text: 'W'
|
||||||
|
MButton:
|
||||||
|
text: 'E'
|
||||||
|
MButton:
|
||||||
|
text: 'R'
|
||||||
|
MButton:
|
||||||
|
text: 'T'
|
||||||
|
MButton:
|
||||||
|
text: 'Y'
|
||||||
|
MButton:
|
||||||
|
text: 'U'
|
||||||
|
MButton:
|
||||||
|
text: 'I'
|
||||||
|
MButton:
|
||||||
|
text: 'O'
|
||||||
|
MButton:
|
||||||
|
text: 'P'
|
||||||
|
BoxLayout:
|
||||||
|
update_amount: root.update_text
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '30dp'
|
||||||
|
MButton:
|
||||||
|
text: 'A'
|
||||||
|
MButton:
|
||||||
|
text: 'S'
|
||||||
|
MButton:
|
||||||
|
text: 'D'
|
||||||
|
MButton:
|
||||||
|
text: 'F'
|
||||||
|
MButton:
|
||||||
|
text: 'G'
|
||||||
|
MButton:
|
||||||
|
text: 'H'
|
||||||
|
MButton:
|
||||||
|
text: 'J'
|
||||||
|
MButton:
|
||||||
|
text: 'K'
|
||||||
|
MButton:
|
||||||
|
text: 'L'
|
||||||
|
BoxLayout:
|
||||||
|
update_amount: root.update_text
|
||||||
|
size_hint: 1, None
|
||||||
|
height: '30dp'
|
||||||
|
MButton:
|
||||||
|
text: 'Z'
|
||||||
|
MButton:
|
||||||
|
text: 'X'
|
||||||
|
MButton:
|
||||||
|
text: 'C'
|
||||||
|
MButton:
|
||||||
|
text: 'V'
|
||||||
|
MButton:
|
||||||
|
text: 'B'
|
||||||
|
MButton:
|
||||||
|
text: 'N'
|
||||||
|
MButton:
|
||||||
|
text: 'M'
|
||||||
|
MButton:
|
||||||
|
text: '<'
|
||||||
|
|
||||||
GridLayout:
|
GridLayout:
|
||||||
rows: 1
|
rows: 1
|
||||||
spacing: '12dp'
|
spacing: '12dp'
|
||||||
|
@ -182,6 +267,7 @@ Builder.load_string('''
|
||||||
id: next
|
id: next
|
||||||
text: _('Next')
|
text: _('Next')
|
||||||
root: root
|
root: root
|
||||||
|
disabled: True
|
||||||
|
|
||||||
|
|
||||||
<ShowSeedDialog>
|
<ShowSeedDialog>
|
||||||
|
@ -206,8 +292,6 @@ Builder.load_string('''
|
||||||
valign: 'middle'
|
valign: 'middle'
|
||||||
font_size: self.width/15
|
font_size: self.width/15
|
||||||
text_size: self.width - dp(24), self.height - dp(12)
|
text_size: self.width - dp(24), self.height - dp(12)
|
||||||
#size_hint: 1, None
|
|
||||||
#height: self.texture_size[1] + dp(24)
|
|
||||||
color: .1, .1, .1, 1
|
color: .1, .1, .1, 1
|
||||||
background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top'
|
background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top'
|
||||||
background_down: self.background_normal
|
background_down: self.background_normal
|
||||||
|
@ -215,7 +299,6 @@ Builder.load_string('''
|
||||||
Label:
|
Label:
|
||||||
rows: 1
|
rows: 1
|
||||||
size_hint: 1, .7
|
size_hint: 1, .7
|
||||||
id: but_seed
|
|
||||||
border: 4, 4, 4, 4
|
border: 4, 4, 4, 4
|
||||||
halign: 'justify'
|
halign: 'justify'
|
||||||
valign: 'middle'
|
valign: 'middle'
|
||||||
|
@ -250,7 +333,6 @@ class WizardDialog(EventsDialog):
|
||||||
Window.bind(size=_trigger_size_dialog,
|
Window.bind(size=_trigger_size_dialog,
|
||||||
rotation=_trigger_size_dialog)
|
rotation=_trigger_size_dialog)
|
||||||
_trigger_size_dialog()
|
_trigger_size_dialog()
|
||||||
Window.softinput_mode = 'pan'
|
|
||||||
|
|
||||||
def _size_dialog(self, dt):
|
def _size_dialog(self, dt):
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
|
@ -273,10 +355,7 @@ class WizardDialog(EventsDialog):
|
||||||
def on_dismiss(self):
|
def on_dismiss(self):
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
if app.wallet is None and self._on_release is not None:
|
if app.wallet is None and self._on_release is not None:
|
||||||
print "on dismiss: stopping app"
|
|
||||||
app.stop()
|
app.stop()
|
||||||
else:
|
|
||||||
Window.softinput_mode = 'below_target'
|
|
||||||
|
|
||||||
|
|
||||||
class CreateRestoreDialog(WizardDialog):
|
class CreateRestoreDialog(WizardDialog):
|
||||||
|
@ -296,12 +375,12 @@ class ShowSeedDialog(WizardDialog):
|
||||||
def on_parent(self, instance, value):
|
def on_parent(self, instance, value):
|
||||||
if value:
|
if value:
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
stepper = self.ids.stepper
|
|
||||||
stepper.opacity = 1
|
|
||||||
stepper.source = 'atlas://gui/kivy/theming/light/stepper_full'
|
|
||||||
self._back = _back = partial(self.ids.back.dispatch, 'on_release')
|
self._back = _back = partial(self.ids.back.dispatch, 'on_release')
|
||||||
|
|
||||||
|
|
||||||
|
class WordButton(Button):
|
||||||
|
pass
|
||||||
|
|
||||||
class RestoreSeedDialog(WizardDialog):
|
class RestoreSeedDialog(WizardDialog):
|
||||||
|
|
||||||
message = StringProperty('')
|
message = StringProperty('')
|
||||||
|
@ -309,10 +388,34 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(RestoreSeedDialog, self).__init__(**kwargs)
|
super(RestoreSeedDialog, self).__init__(**kwargs)
|
||||||
self._test = kwargs['test']
|
self._test = kwargs['test']
|
||||||
self._trigger_check_seed = Clock.create_trigger(self.check_seed)
|
from electrum.mnemonic import Mnemonic
|
||||||
|
self.mnemonic = Mnemonic('en')
|
||||||
|
|
||||||
def check_seed(self, dt):
|
def on_text(self, dt):
|
||||||
self.ids.next.disabled = not bool(self._test(self.get_seed_text()))
|
text = self.get_seed_text()
|
||||||
|
self.ids.next.disabled = not bool(self._test(text))
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
last_word = ''
|
||||||
|
elif text[-1] == ' ':
|
||||||
|
last_word = ''
|
||||||
|
else:
|
||||||
|
last_word = text.split(' ')[-1]
|
||||||
|
|
||||||
|
self.ids.suggestions.clear_widgets()
|
||||||
|
suggestions = [x for x in self.mnemonic.get_suggestions(last_word)]
|
||||||
|
if suggestions and len(suggestions) < 10:
|
||||||
|
for w in suggestions:
|
||||||
|
b = WordButton(text=w)
|
||||||
|
self.ids.suggestions.add_widget(b)
|
||||||
|
|
||||||
|
def on_word(self, w):
|
||||||
|
text = self.get_seed_text()
|
||||||
|
words = text.split(' ')
|
||||||
|
words[-1] = w
|
||||||
|
text = ' '.join(words)
|
||||||
|
self.ids.text_input_seed.text = text + ' '
|
||||||
|
self.ids.suggestions.clear_widgets()
|
||||||
|
|
||||||
def get_seed_text(self):
|
def get_seed_text(self):
|
||||||
ti = self.ids.text_input_seed
|
ti = self.ids.text_input_seed
|
||||||
|
@ -320,6 +423,15 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
text = ' '.join(text.split())
|
text = ' '.join(text.split())
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
def update_text(self, c):
|
||||||
|
c = c.lower()
|
||||||
|
text = self.ids.text_input_seed.text
|
||||||
|
if c == '<':
|
||||||
|
text = text[:-1]
|
||||||
|
else:
|
||||||
|
text += c
|
||||||
|
self.ids.text_input_seed.text = text
|
||||||
|
|
||||||
def scan_seed(self):
|
def scan_seed(self):
|
||||||
def on_complete(text):
|
def on_complete(text):
|
||||||
self.ids.text_input_seed.text = text
|
self.ids.text_input_seed.text = text
|
||||||
|
@ -330,15 +442,10 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
if value:
|
if value:
|
||||||
tis = self.ids.text_input_seed
|
tis = self.ids.text_input_seed
|
||||||
tis.focus = True
|
tis.focus = True
|
||||||
tis._keyboard.bind(on_key_down=self.on_key_down)
|
#tis._keyboard.bind(on_key_down=self.on_key_down)
|
||||||
stepper = self.ids.stepper
|
|
||||||
stepper.opacity = 1
|
|
||||||
stepper.source = ('atlas://gui/kivy/theming'
|
|
||||||
'/light/stepper_restore_seed')
|
|
||||||
self._back = _back = partial(self.ids.back.dispatch,
|
self._back = _back = partial(self.ids.back.dispatch,
|
||||||
'on_release')
|
'on_release')
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
#app.navigation_higherarchy.append(_back)
|
|
||||||
|
|
||||||
def on_key_down(self, keyboard, keycode, key, modifiers):
|
def on_key_down(self, keyboard, keycode, key, modifiers):
|
||||||
if keycode[0] in (13, 271):
|
if keycode[0] in (13, 271):
|
||||||
|
@ -359,7 +466,7 @@ class RestoreSeedDialog(WizardDialog):
|
||||||
tis.focus = False
|
tis.focus = False
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self._remove_keyboard()
|
#self._remove_keyboard()
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
#if self._back in app.navigation_higherarchy:
|
#if self._back in app.navigation_higherarchy:
|
||||||
# app.navigation_higherarchy.pop()
|
# app.navigation_higherarchy.pop()
|
||||||
|
|
|
@ -132,6 +132,11 @@ class Mnemonic(object):
|
||||||
words.append(self.wordlist[x])
|
words.append(self.wordlist[x])
|
||||||
return ' '.join(words)
|
return ' '.join(words)
|
||||||
|
|
||||||
|
def get_suggestions(self, prefix):
|
||||||
|
for w in self.wordlist:
|
||||||
|
if w.startswith(prefix) and w!=prefix:
|
||||||
|
yield w
|
||||||
|
|
||||||
def mnemonic_decode(self, seed):
|
def mnemonic_decode(self, seed):
|
||||||
n = len(self.wordlist)
|
n = len(self.wordlist)
|
||||||
words = seed.split()
|
words = seed.split()
|
||||||
|
|
Loading…
Reference in New Issue