Пример #1
0
class PinDialog(ui.Layout):
    def __init__(self, prompt, subprompt, allow_cancel=True, maxlength=9):
        self.maxlength = maxlength
        self.input = PinInput(prompt, subprompt, "")

        icon_confirm = res.load(ui.ICON_CONFIRM)
        self.confirm_button = Button(ui.grid(14), icon_confirm, ButtonConfirm)
        self.confirm_button.on_click = self.on_confirm

        icon_back = res.load(ui.ICON_BACK)
        self.reset_button = Button(ui.grid(12), icon_back, ButtonClear)
        self.reset_button.on_click = self.on_reset

        if allow_cancel:
            icon_lock = res.load(ui.ICON_LOCK)
            self.cancel_button = Button(ui.grid(12), icon_lock, ButtonCancel)
            self.cancel_button.on_click = self.on_cancel
        else:
            self.cancel_button = Button(ui.grid(12), "")
            self.cancel_button.disable()

        self.pin_buttons = [
            PinButton(i, d, self) for i, d in enumerate(generate_digits())
        ]

    def dispatch(self, event, x, y):
        for btn in self.pin_buttons:
            btn.dispatch(event, x, y)
        self.input.dispatch(event, x, y)
        self.confirm_button.dispatch(event, x, y)
        if self.input.pin:
            self.reset_button.dispatch(event, x, y)
        else:
            self.cancel_button.dispatch(event, x, y)

    def assign(self, pin):
        if len(pin) > self.maxlength:
            return
        for btn in self.pin_buttons:
            if len(pin) < self.maxlength:
                btn.enable()
            else:
                btn.disable()
        if pin:
            self.reset_button.enable()
            self.cancel_button.disable()
        else:
            self.reset_button.disable()
            self.cancel_button.enable()
        self.input.pin = pin
        self.input.repaint = True

    def on_reset(self):
        self.assign("")

    def on_cancel(self):
        raise ui.Result(CANCELLED)

    def on_confirm(self):
        raise ui.Result(self.input.pin)
Пример #2
0
class NumInput(ui.Component):
    def __init__(self,
                 count: int = 5,
                 max_count: int = 16,
                 min_count: int = 1) -> None:
        super().__init__()
        self.count = count
        self.max_count = max_count
        self.min_count = min_count

        self.minus = Button(ui.grid(3), "-")
        self.minus.on_click = self.on_minus  # type: ignore
        self.plus = Button(ui.grid(5), "+")
        self.plus.on_click = self.on_plus  # type: ignore
        self.text = Label(ui.grid(4), "", LABEL_CENTER, ui.BOLD)

        self.edit(count)

    def dispatch(self, event: int, x: int, y: int) -> None:
        self.minus.dispatch(event, x, y)
        self.plus.dispatch(event, x, y)
        self.text.dispatch(event, x, y)

    def on_minus(self) -> None:
        self.edit(self.count - 1)

    def on_plus(self) -> None:
        self.edit(self.count + 1)

    def edit(self, count: int) -> None:
        count = max(count, self.min_count)
        count = min(count, self.max_count)
        if self.count != count:
            self.on_change(count)
        self.count = count
        self.text.content = str(count)
        self.text.repaint = True
        if self.count == self.min_count:
            self.minus.disable()
        else:
            self.minus.enable()
        if self.count == self.max_count:
            self.plus.disable()
        else:
            self.plus.enable()

    def on_change(self, count: int) -> None:
        pass
Пример #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.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()
Пример #4
0
class PassphraseKeyboard(ui.Layout):
    def __init__(self, prompt: str, max_length: int, page: int = 1) -> None:
        super().__init__()
        self.prompt = Prompt(prompt)
        self.max_length = max_length
        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), ButtonClear)
        self.back.on_click = self.on_back_click  # type: ignore
        self.back.disable()

        self.done = Button(ui.grid(14), res.load(ui.ICON_CONFIRM),
                           ButtonConfirm)
        self.done.on_click = self.on_confirm  # type: ignore

        self.keys = key_buttons(KEYBOARD_KEYS[self.page], self)
        self.pending_button: Optional[KeyButton] = None
        self.pending_index = 0

    def dispatch(self, event: int, x: int, y: int) -> None:
        if self.input.text:
            self.input.dispatch(event, x, y)
        else:
            self.prompt.dispatch(event, x, y)
        self.back.dispatch(event, x, y)
        self.done.dispatch(event, x, y)
        for btn in self.keys:
            btn.dispatch(event, x, y)

        if event == ui.RENDER:
            render_scrollbar(self.page)

    def on_back_click(self) -> None:
        # Backspace was clicked.  If we have any content in the input, let's delete
        # the last character.  Otherwise cancel.
        text = self.input.text
        if text:
            self.edit(text[:-1])
        else:
            self.on_cancel()

    def on_key_click(self, button: KeyButton) -> None:
        # Key button was clicked.  If this button is pending, let's cycle the
        # pending character in input.  If not, let's just append the first
        # character.
        button_text = button.get_text_content()
        if self.pending_button is button:
            index = (self.pending_index + 1) % len(button_text)
            prefix = self.input.text[:-1]
        else:
            index = 0
            prefix = self.input.text
        if len(button_text) > 1:
            self.edit(prefix + button_text[index], button, index)
        else:
            self.edit(prefix + button_text[index])

    def on_timeout(self) -> None:
        # Timeout occurred, let's just reset the pending marker.
        self.edit(self.input.text)

    def edit(self,
             text: str,
             button: KeyButton = None,
             index: int = 0) -> None:
        if len(text) > self.max_length:
            return

        self.pending_button = button
        self.pending_index = index

        # modify the input state
        pending = button is not None
        self.input.edit(text, pending)

        if text:
            self.back.enable()
        else:
            self.back.disable()
            self.prompt.repaint = True

    async def handle_input(self) -> None:
        touch = loop.wait(io.TOUCH)
        timeout = loop.sleep(1000)
        race_touch = loop.race(touch)
        race_timeout = loop.race(touch, timeout)

        while True:
            if self.pending_button is not None:
                race = race_timeout
            else:
                race = race_touch
            result = await race

            if touch in race.finished:
                event, x, y = result
                workflow.idle_timer.touch()
                self.dispatch(event, x, y)
            else:
                self.on_timeout()

    async def handle_paging(self) -> None:
        swipe = await Swipe(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)
        self.back.repaint = True
        self.done.repaint = True
        self.input.repaint = True
        self.prompt.repaint = True

    def on_cancel(self) -> None:
        raise ui.Result(CANCELLED)

    def on_confirm(self) -> None:
        raise ui.Result(self.input.text)

    def create_tasks(self) -> Tuple[loop.Task, ...]:
        tasks: Tuple[loop.Task, ...] = (
            self.handle_input(),
            self.handle_rendering(),
            self.handle_paging(),
        )

        if __debug__:
            from apps.debug import input_signal

            return tasks + (input_signal(), )
        else:
            return tasks
Пример #5
0
class PinDialog(ui.Layout):
    def __init__(
        self,
        prompt: str,
        subprompt: Optional[str],
        allow_cancel: bool = True,
        maxlength: int = 9,
    ) -> None:
        self.maxlength = maxlength
        self.input = PinInput(prompt, subprompt, "")

        icon_confirm = res.load(ui.ICON_CONFIRM)
        self.confirm_button = Button(ui.grid(14), icon_confirm, ButtonConfirm)
        self.confirm_button.on_click = self.on_confirm  # type: ignore
        self.confirm_button.disable()

        icon_back = res.load(ui.ICON_BACK)
        self.reset_button = Button(ui.grid(12), icon_back, ButtonClear)
        self.reset_button.on_click = self.on_reset  # type: ignore

        if allow_cancel:
            icon_lock = res.load(
                ui.ICON_CANCEL if config.is_unlocked() else ui.ICON_LOCK)
            self.cancel_button = Button(ui.grid(12), icon_lock, ButtonCancel)
            self.cancel_button.on_click = self.on_cancel  # type: ignore
        else:
            self.cancel_button = Button(ui.grid(12), "")
            self.cancel_button.disable()

        self.pin_buttons = [
            PinButton(i, d, self) for i, d in enumerate(generate_digits())
        ]

    def dispatch(self, event: int, x: int, y: int) -> None:
        self.input.dispatch(event, x, y)
        if self.input.pin:
            self.reset_button.dispatch(event, x, y)
        else:
            self.cancel_button.dispatch(event, x, y)
        self.confirm_button.dispatch(event, x, y)
        for btn in self.pin_buttons:
            btn.dispatch(event, x, y)

    def assign(self, pin: str) -> None:
        if len(pin) > self.maxlength:
            return
        for btn in self.pin_buttons:
            if len(pin) < self.maxlength:
                btn.enable()
            else:
                btn.disable()
        if pin:
            self.confirm_button.enable()
            self.reset_button.enable()
            self.cancel_button.disable()
        else:
            self.confirm_button.disable()
            self.reset_button.disable()
            self.cancel_button.enable()
        self.input.pin = pin
        self.input.repaint = True

    def on_reset(self) -> None:
        self.assign("")

    def on_cancel(self) -> None:
        raise ui.Result(CANCELLED)

    def on_confirm(self) -> None:
        if self.input.pin:
            raise ui.Result(self.input.pin)

    if __debug__:

        def create_tasks(self) -> Tuple[loop.Task, ...]:
            from apps.debug import input_signal

            return super().create_tasks() + (input_signal(), )
Пример #6
0
class PassphraseKeyboard(ui.Layout):
    def __init__(self, prompt, max_length, page=1):
        self.prompt = Prompt(prompt)
        self.max_length = max_length
        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), ButtonClear)
        self.back.on_click = self.on_back_click
        self.back.disable()

        self.done = Button(ui.grid(14), res.load(ui.ICON_CONFIRM),
                           ButtonConfirm)
        self.done.on_click = self.on_confirm

        self.keys = key_buttons(KEYBOARD_KEYS[self.page], self)
        self.pending_button = None
        self.pending_index = 0

    def dispatch(self, event, x, y):
        if self.input.content:
            self.input.dispatch(event, x, y)
        else:
            self.prompt.dispatch(event, x, y)
        self.back.dispatch(event, x, y)
        self.done.dispatch(event, x, y)
        for btn in self.keys:
            btn.dispatch(event, x, y)

        if event == ui.RENDER:
            render_scrollbar(self.page)

    def on_back_click(self):
        # Backspace was clicked.  If we have any content in the input, let's delete
        # the last character.  Otherwise cancel.
        content = self.input.content
        if content:
            self.edit(content[:-1])
        else:
            self.on_cancel()

    def on_key_click(self, button: KeyButton):
        # Key button was clicked.  If this button is pending, let's cycle the
        # pending character in input.  If not, let's just append the first
        # character.
        button_text = button.get_text_content()
        if self.pending_button is button:
            index = (self.pending_index + 1) % len(button_text)
            prefix = self.input.content[:-1]
        else:
            index = 0
            prefix = self.input.content
        if len(button_text) > 1:
            self.edit(prefix + button_text[index], button, index)
        else:
            self.edit(prefix + button_text[index])

    def on_timeout(self):
        # Timeout occurred, let's just reset the pending marker.
        self.edit(self.input.content)

    def edit(self, content: str, button: Button = None, index: int = 0):
        if len(content) > self.max_length:
            return

        self.pending_button = button
        self.pending_index = index

        # modify the input state
        pending = button is not None
        self.input.edit(content, pending)

        if content:
            self.back.enable()
        else:
            self.back.disable()
            self.prompt.repaint = True

    async def handle_input(self):
        touch = loop.wait(io.TOUCH)
        timeout = loop.sleep(1000 * 1000 * 1)
        spawn_touch = loop.spawn(touch)
        spawn_timeout = loop.spawn(touch, timeout)

        while True:
            if self.pending_button is not None:
                spawn = spawn_timeout
            else:
                spawn = spawn_touch
            result = await spawn

            if touch in spawn.finished:
                event, x, y = result
                self.dispatch(event, x, y)
            else:
                self.on_timeout()

    async def handle_paging(self):
        swipe = await Swipe(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)
        self.back.repaint = True
        self.done.repaint = True
        self.input.repaint = True
        self.prompt.repaint = True

    def on_cancel(self):
        raise ui.Result(CANCELLED)

    def on_confirm(self):
        raise ui.Result(self.input.content)

    def create_tasks(self):
        return self.handle_input(), self.handle_rendering(
        ), self.handle_paging()