kivy wizard: use own soft keyboard

This commit is contained in:
ThomasV 2016-02-13 15:10:17 +01:00
parent 950f3ae633
commit 656069070a
3 changed files with 168 additions and 56 deletions

View File

@ -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:'):

View File

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

View File

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