예제 #1
0
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
예제 #2
0
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()
예제 #3
0
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()
예제 #4
0
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()