class MnemonicKeyboard(ui.Widget): def __init__(self, prompt: str=''): self.prompt = prompt self.input = Input(ui.grid(1, n_x=4, n_y=4, cells_x=3), '', '') self.back = Button(ui.grid(0, n_x=4, n_y=4), res.load(ui.ICON_BACK), style=ui.BTN_CLEAR) self.keys = key_buttons(MNEMONIC_KEYS) self.pbutton = None # pending key button self.pindex = 0 # index of current pending char in pbutton def render(self): if self.input.content: # content button and backspace self.input.render() self.back.render() else: # prompt display.bar(0, 8, ui.WIDTH, 60, ui.BG) display.text(20, 40, self.prompt, ui.BOLD, ui.GREY, ui.BG) # key buttons for btn in self.keys: btn.render() def touch(self, event, pos): content = self.input.content word = self.input.word if self.back.touch(event, pos) == BTN_CLICKED: # backspace, delete the last character of input self.edit(content[:-1]) return if self.input.touch(event, pos) == BTN_CLICKED: # input press, either auto-complete or confirm if word and content == word: self.edit('') return content else: self.edit(word) return for btn in self.keys: if btn.touch(event, pos) == BTN_CLICKED: # key press, add new char to input or cycle the pending button if self.pbutton is btn: index = (self.pindex + 1) % len(btn.content) content = content[:-1] + btn.content[index] else: index = 0 content += btn.content[0] self.edit(content, btn, index) return def edit(self, content, button=None, index=0): word = bip39.find_word(content) or '' mask = bip39.complete_word(content) self.pbutton = button self.pindex = index self.input.edit(content, word, button is not None) # enable or disable key buttons for btn in self.keys: if btn is button or compute_mask(btn.content) & mask: btn.enable() else: btn.disable() async def __iter__(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait_timeout = loop.wait(touch, timeout) wait_touch = loop.wait(touch) content = None self.back.taint() self.input.taint() while content is None: self.render() if self.pbutton is not None: wait = wait_timeout else: wait = wait_touch result = await wait if touch in wait.finished: event, *pos = result content = self.touch(event, pos) else: if self.input.word: # just reset the pending state self.edit(self.input.content) else: # invalid character, backspace it self.edit(self.input.content[:-1]) return content
class KeyboardMultiTap(ui.Widget): def __init__(self, content=''): self.content = content self.sugg_mask = 0xffffffff self.sugg_word = None self.pending_button = None self.pending_index = 0 self.key_buttons = key_buttons() self.sugg_button = Button((5, 5, 240 - 35, 30), '') self.bs_button = Button((240 - 35, 5, 30, 30), res.load('trezor/res/pin_close.toig'), normal_style=CLEAR_BUTTON, active_style=CLEAR_BUTTON_ACTIVE) def render(self): # clear canvas under input line display.bar(0, 0, 205, 40, ui.BLACK) # input line content_width = display.text_width(self.content, ui.BOLD) display.text(20, 30, self.content, ui.BOLD, ui.WHITE, ui.BLACK) # pending marker if self.pending_button is not None: pending_width = display.text_width(self.content[-1:], ui.BOLD) pending_x = 20 + content_width - pending_width display.bar(pending_x, 33, pending_width + 2, 3, ui.WHITE) # auto-suggest if self.sugg_word is not None: sugg_rest = self.sugg_word[len(self.content):] sugg_x = 20 + content_width display.text(sugg_x, 30, sugg_rest, ui.BOLD, ui.GREY, ui.BLACK) # render backspace button if self.content: self.bs_button.render() else: display.bar(240 - 48, 0, 48, 42, ui.BLACK) # key buttons for btn in self.key_buttons: btn.render() def touch(self, event, pos): if self.bs_button.touch(event, pos) == BTN_CLICKED: self.content = self.content[:-1] self.pending_button = None self.pending_index = 0 self._update_suggestion() self._update_buttons() return if self.sugg_button.touch( event, pos) == BTN_CLICKED and self.sugg_word is not None: self.content = self.sugg_word self.pending_button = None self.pending_index = 0 self._update_suggestion() self._update_buttons() return for btn in self.key_buttons: if btn.touch(event, pos) == BTN_CLICKED: if self.pending_button is btn: self.pending_index = (self.pending_index + 1) % len( btn.content) self.content = self.content[:-1] self.content += btn.content[self.pending_index] self._update_suggestion() else: self.content += btn.content[0] self._update_suggestion() self.pending_button = btn self.pending_index = 0 return def _update_suggestion(self): if self.content: self.sugg_word = bip39.find_word(self.content) self.sugg_mask = bip39.complete_word(self.content) else: self.sugg_word = None self.sugg_mask = 0xffffffff def _update_buttons(self): for btn in self.key_buttons: if compute_mask(btn.content) & self.sugg_mask: btn.enable() else: btn.disable() def __iter__(self): timeout = loop.Sleep(1000 * 1000 * 1) touch = loop.Select(loop.TOUCH) wait = loop.Wait((touch, timeout)) while True: self.render() result = yield wait if touch in wait.finished: event, *pos = result self.touch(event, pos) else: self.pending_button = None self.pending_index = 0 self._update_suggestion() self._update_buttons()
class PassphraseKeyboard(ui.Widget): def __init__(self, prompt, page=1): self.prompt = Prompt(prompt) self.page = page self.input = Input(ui.grid(0, n_x=1, n_y=6), '') self.back = Button(ui.grid(12), res.load(ui.ICON_BACK), style=ui.BTN_CLEAR) self.done = Button(ui.grid(14), res.load(ui.ICON_CONFIRM), style=ui.BTN_CONFIRM) self.keys = key_buttons(KEYBOARD_KEYS[self.page]) self.pbutton = None # pending key button self.pindex = 0 # index of current pending char in pbutton def render(self): # passphrase or prompt if self.input.content: self.input.render() else: self.prompt.render() render_scrollbar(self.page) # buttons self.back.render() self.done.render() for btn in self.keys: btn.render() def touch(self, event, pos): content = self.input.content if self.back.touch(event, pos) == BTN_CLICKED: if content: # backspace, delete the last character of input self.edit(content[:-1]) return else: # cancel return CANCELLED if self.done.touch(event, pos) == BTN_CLICKED: # confirm button, return the content return content for btn in self.keys: if btn.touch(event, pos) == BTN_CLICKED: if isinstance(btn.content[0], str): # key press, add new char to input or cycle the pending button if self.pbutton is btn: index = (self.pindex + 1) % len(btn.content) content = content[:-1] + btn.content[index] else: index = 0 content += btn.content[0] else: index = 0 content += ' ' self.edit(content, btn, index) return def edit(self, content, button=None, index=0): if button and len(button.content) == 1: # one-letter buttons are never pending button = None index = 0 self.pbutton = button self.pindex = index self.input.edit(content, button is not None) if content: self.back.enable() else: self.back.disable() self.prompt.taint() async def __iter__(self): self.edit(self.input.content) # init button state while True: change = self.change_page() enter = self.enter_text() wait = loop.wait(change, enter) result = await wait if enter in wait.finished: return result @ui.layout async def enter_text(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.TOUCH) wait_timeout = loop.wait(touch, timeout) wait_touch = loop.wait(touch) content = None while content is None: self.render() if self.pbutton is not None: wait = wait_timeout else: wait = wait_touch result = await wait if touch in wait.finished: event, *pos = result content = self.touch(event, pos) else: # disable the pending buttons self.edit(self.input.content) return content async def change_page(self): swipe = await Swipe(directions=SWIPE_HORIZONTAL) if swipe == SWIPE_LEFT: self.page = (self.page + 1) % len(KEYBOARD_KEYS) else: self.page = (self.page - 1) % len(KEYBOARD_KEYS) self.keys = key_buttons(KEYBOARD_KEYS[self.page]) self.back.taint() self.done.taint() self.input.taint() self.prompt.taint()
class KeyboardZooming(ui.Widget): def __init__(self, content='', uppercase=True): self.content = content self.uppercase = uppercase self.zoom_buttons = None self.key_buttons = key_buttons() self.bs_button = Button((240 - 35, 5, 30, 30), res.load('trezor/res/pin_close.toig'), normal_style=CLEAR_BUTTON, active_style=CLEAR_BUTTON_ACTIVE) def render(self): self.render_input() if self.zoom_buttons: for btn in self.zoom_buttons: btn.render() else: for btn in self.key_buttons: btn.render() def render_input(self): if self.content: display.bar(0, 0, 200, 40, ui.BLACK) else: display.bar(0, 0, 240, 40, ui.BLACK) display.text(20, 30, self.content, ui.BOLD, ui.GREY, ui.BLACK) if self.content: self.bs_button.render() def touch(self, event, pos): if self.bs_button.touch(event, pos) == BTN_CLICKED: self.content = self.content[:-1] self.bs_button.taint() return if self.zoom_buttons: return self.touch_zoom(event, pos) else: return self.touch_keyboard(event, pos) def touch_zoom(self, event, pos): for btn in self.zoom_buttons: if btn.touch(event, pos) == BTN_CLICKED: self.content += btn.content self.zoom_buttons = None for btn in self.key_buttons: btn.taint() self.bs_button.taint() break def touch_keyboard(self, event, pos): for btn in self.key_buttons: if btn.touch(event, pos) == BTN_CLICKED: self.zoom_buttons = zoom_buttons(btn.content, self.uppercase) for btn in self.zoom_buttons: btn.taint() self.bs_button.taint() break def __iter__(self): timeout = loop.Sleep(1000 * 1000 * 1) touch = loop.Select(loop.TOUCH) wait = loop.Wait((touch, timeout)) while True: self.render() result = yield wait if touch in wait.finished: event, *pos = result self.touch(event, pos) elif self.zoom_buttons: self.zoom_buttons = None for btn in self.key_buttons: btn.taint()