class ConfirmDialog(Widget): def __init__(self, content, confirm=DEFAULT_CONFIRM, cancel=DEFAULT_CANCEL, confirm_style=ui.BTN_CONFIRM, cancel_style=ui.BTN_CANCEL): self.content = content if cancel is not None: self.confirm = Button(ui.grid(9, n_x=2), confirm, style=confirm_style) self.cancel = Button(ui.grid(8, n_x=2), cancel, style=cancel_style) else: self.confirm = Button(ui.grid(4, n_x=1), confirm, style=confirm_style) self.cancel = None def render(self): self.confirm.render() if self.cancel is not None: self.cancel.render() def touch(self, event, pos): if self.confirm.touch(event, pos) == BTN_CLICKED: return CONFIRMED if self.cancel is not None: if self.cancel.touch(event, pos) == BTN_CLICKED: return CANCELLED async def __iter__(self): return await loop.wait(super().__iter__(), self.content)
class HoldToConfirmDialog(Widget): def __init__( self, content, hold="Hold to confirm", button_style=ui.BTN_CONFIRM, loader_style=ui.LDR_DEFAULT, ): self.content = content self.button = Button(ui.grid(4, n_x=1), hold, style=button_style) self.loader = Loader(style=loader_style) if content.__class__.__iter__ is not Widget.__iter__: raise TypeError( "HoldToConfirmDialog does not support widgets with custom event loop" ) def taint(self): super().taint() self.button.taint() self.content.taint() def render(self): self.button.render() if not self.loader.is_active(): self.content.render() def touch(self, event, pos): button = self.button was_active = button.state == BTN_ACTIVE button.touch(event, pos) is_active = button.state == BTN_ACTIVE if is_active and not was_active: ui.display.clear() self.loader.start() return _STARTED if was_active and not is_active: self.content.taint() if self.loader.stop(): return CONFIRMED else: return _STOPPED async def __iter__(self): result = None while result is None or result < 0: # _STARTED or _STOPPED if self.loader.is_active(): if __debug__: result = await loop.spawn(self.loader, super().__iter__(), confirm_signal) else: result = await loop.spawn(self.loader, super().__iter__()) else: if __debug__: result = await loop.spawn(super().__iter__(), confirm_signal) else: result = await super().__iter__() return result
class PinMatrix(ui.Widget): def __init__(self, label, pin=''): self.label = label self.pin = pin self.digits = generate_digits() # we lay out the buttons top-left to bottom-right, but the order of the # digits is defined as bottom-left to top-right (on numpad) reordered_digits = self.digits[6:] + self.digits[3:6] + self.digits[:3] self.pin_buttons = [ Button(digit_area(i), str(d)) for i, d in enumerate(reordered_digits) ] self.clear_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): header = '*' * len(self.pin) if self.pin else self.label # clear canvas under input line display.bar(0, 0, 205, 48, ui.BLACK) # input line with a header display.text_center(120, 30, header, ui.BOLD, ui.GREY, ui.BLACK) # render clear button if self.pin: self.clear_button.render() else: display.bar(240 - 48, 0, 48, 42, ui.BLACK) # pin matrix buttons for btn in self.pin_buttons: btn.render() # vertical border bars # display.bar(79, 48, 2, 143, ui.blend(ui.BLACK, ui.WHITE, 0.25)) # display.bar(158, 48, 2, 143, ui.blend(ui.BLACK, ui.WHITE, 0.25)) # horizontal border bars # display.bar(0, 95, 240, 2, ui.blend(ui.BLACK, ui.WHITE, 0.25)) # display.bar(0, 142, 240, 2, ui.blend(ui.BLACK, ui.WHITE, 0.25)) def touch(self, event, pos): if self.clear_button.touch(event, pos) == BTN_CLICKED: self.pin = '' for btn in self.pin_buttons: if btn.touch(event, pos) == BTN_CLICKED: if len(self.pin) < 9: self.pin += btn.content
class HoldToConfirmDialog(Widget): def __init__( self, content, hold="Hold to confirm", button_style=ui.BTN_CONFIRM, loader_style=ui.LDR_DEFAULT, ): self.content = content self.button = Button(ui.grid(4, n_x=1), hold, style=button_style) self.loader = Loader(style=loader_style) def taint(self): super().taint() self.button.taint() self.content.taint() def render(self): self.button.render() def touch(self, event, pos): button = self.button was_active = button.state == BTN_ACTIVE button.touch(event, pos) is_active = button.state == BTN_ACTIVE if is_active and not was_active: ui.display.clear() self.loader.start() return _STARTED if was_active and not is_active: self.content.taint() if self.loader.stop(): return CONFIRMED else: return _STOPPED async def __iter__(self): result = None while result is None or result < 0: # _STARTED or _STOPPED if self.loader.is_active(): content_loop = self.loader else: content_loop = self.content confirm_loop = super().__iter__() # default loop (render on touch) if __debug__: result = await loop.spawn(content_loop, confirm_loop, confirm_signal) else: result = await loop.spawn(content_loop, confirm_loop) return result
class WordSelector(Widget): def __init__(self, content): self.content = content self.w12 = Button(ui.grid(8, n_y=4, n_x=4, cells_x=2), str(_W12), style=ui.BTN_KEY) self.w15 = Button(ui.grid(10, n_y=4, n_x=4, cells_x=2), str(_W15), style=ui.BTN_KEY) self.w18 = Button(ui.grid(12, n_y=4, n_x=4, cells_x=2), str(_W18), style=ui.BTN_KEY) self.w24 = Button(ui.grid(14, n_y=4, n_x=4, cells_x=2), str(_W24), style=ui.BTN_KEY) def render(self): self.w12.render() self.w15.render() self.w18.render() self.w24.render() def touch(self, event, pos): if self.w12.touch(event, pos) == BTN_CLICKED: return _W12 if self.w15.touch(event, pos) == BTN_CLICKED: return _W15 if self.w18.touch(event, pos) == BTN_CLICKED: return _W18 if self.w24.touch(event, pos) == BTN_CLICKED: return _W24 async def __iter__(self): return await loop.wait(super().__iter__(), self.content)
class EntrySelector(Widget): def __init__(self, content): self.content = content self.device = Button(ui.grid(8, n_y=4, n_x=4, cells_x=4), "Device") self.host = Button(ui.grid(12, n_y=4, n_x=4, cells_x=4), "Host") def render(self): self.device.render() self.host.render() def touch(self, event, pos): if self.device.touch(event, pos) == BTN_CLICKED: return DEVICE if self.host.touch(event, pos) == BTN_CLICKED: return HOST async def __iter__(self): return await loop.spawn(super().__iter__(), self.content)
class HoldToConfirmDialog(Widget): def __init__(self, content, hold='Hold to confirm', *args, **kwargs): self.content = content self.button = Button(ui.grid(4, n_x=1), hold, normal_style=ui.BTN_CONFIRM, active_style=ui.BTN_CONFIRM_ACTIVE) self.loader = Loader(*args, **kwargs) def render(self): self.button.render() def touch(self, event, pos): button = self.button was_started = button.state & BTN_STARTED and button.state & BTN_ACTIVE button.touch(event, pos) is_started = button.state & BTN_STARTED and button.state & BTN_ACTIVE if is_started and not was_started: ui.display.clear() self.loader.start() return _STARTED if was_started and not is_started: if self.loader.stop(): return CONFIRMED else: return _STOPPED async def __iter__(self): result = None while result is None or result < 0: # _STARTED or _STOPPED if self.loader.is_active(): content_loop = self.loader else: content_loop = self.content confirm_loop = super().__iter__() # default loop (render on touch) result = await loop.wait(content_loop, confirm_loop) return result
class ConfirmDialog(Widget): def __init__(self, content, confirm=DEFAULT_CONFIRM, cancel=DEFAULT_CANCEL): self.content = content if cancel is not None: self.confirm = Button((121, 240 - 48, 119, 48), confirm, normal_style=ui.BTN_CONFIRM, active_style=ui.BTN_CONFIRM_ACTIVE) self.cancel = Button((0, 240 - 48, 119, 48), cancel, normal_style=ui.BTN_CANCEL, active_style=ui.BTN_CANCEL_ACTIVE) else: self.cancel = None self.confirm = Button((0, 240 - 48, 240, 48), confirm, normal_style=ui.BTN_CONFIRM, active_style=ui.BTN_CONFIRM_ACTIVE) def render(self): self.confirm.render() if self.cancel is not None: self.cancel.render() def touch(self, event, pos): if self.confirm.touch(event, pos) == BTN_CLICKED: return CONFIRMED if self.cancel is not None: if self.cancel.touch(event, pos) == BTN_CLICKED: return CANCELLED async def __iter__(self): return await loop.wait(super().__iter__(), self.content)
class WordSelector(Widget): def __init__(self, content): self.content = content self.w12 = Button(ui.grid(6, n_y=4, n_x=3, cells_y=2), str(_W12), style=ui.BTN_KEY) self.w18 = Button(ui.grid(7, n_y=4, n_x=3, cells_y=2), str(_W18), style=ui.BTN_KEY) self.w24 = Button(ui.grid(8, n_y=4, n_x=3, cells_y=2), str(_W24), style=ui.BTN_KEY) def taint(self): super().taint() self.w12.taint() self.w18.taint() self.w24.taint() def render(self): self.w12.render() self.w18.render() self.w24.render() def touch(self, event, pos): if self.w12.touch(event, pos) == BTN_CLICKED: return _W12 if self.w18.touch(event, pos) == BTN_CLICKED: return _W18 if self.w24.touch(event, pos) == BTN_CLICKED: return _W24 async def __iter__(self): if __debug__: result = await loop.spawn(super().__iter__(), self.content, input_signal) if isinstance(result, str): return int(result) else: return result else: return await loop.spawn(super().__iter__(), self.content)
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, 240, 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 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 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.spawn(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.wait(io.TOUCH) wait_timeout = loop.spawn(touch, timeout) wait_touch = loop.spawn(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 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=ui.BTN_CLEAR, active_style=ui.BTN_CLEAR_ACTIVE) def render(self): # clear canvas under input line display.bar(0, 0, 205, 40, ui.BG) # input line content_width = display.text_width(self.content, ui.BOLD) display.text(20, 30, self.content, ui.BOLD, ui.FG, ui.BG) # 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.FG) # 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.BG) # render backspace button if self.content: self.bs_button.render() else: display.bar(240 - 48, 0, 48, 42, ui.BG) # 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(io.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 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=ui.BTN_CLEAR, active_style=ui.BTN_CLEAR_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.BG) else: display.bar(0, 0, 240, 40, ui.BG) display.text(20, 30, self.content, ui.BOLD, ui.GREY, ui.BG) 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 b in self.key_buttons: b.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 b in self.zoom_buttons: b.taint() self.bs_button.taint() break def __iter__(self): timeout = loop.sleep(1000 * 1000 * 1) touch = loop.select(io.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()