diff --git a/src/apps/playground/__init__.py b/src/apps/playground/__init__.py index c083b573e..64858e833 100644 --- a/src/apps/playground/__init__.py +++ b/src/apps/playground/__init__.py @@ -3,56 +3,12 @@ from trezor import ui from trezor.utils import unimport_func -def multiplex_touch_events(gens): - while True: - event = yield loop.Wait([ - loop.TOUCH_START, - loop.TOUCH_MOVE, - loop.TOUCH_END, - ]) - for gen in gens: - gen.send(event) - - def in_area(pos, area): x, y = pos ax, ay, aw, ah = area return ax <= x <= ax + aw and ay <= y <= ay + ah -def click_in(area, click=None, enter=None, leave=None): - while True: - e, pos = yield - if e is not loop.TOUCH_START or not in_area(pos, area): - continue - - inside = True - if enter: - enter() - - while True: - e, pos = yield - if e is loop.TOUCH_MOVE: - if in_area(pos, area): - if not inside: - if enter: - enter() - inside = True - else: - if inside: - if leave: - leave() - inside = False - elif e is loop.TOUCH_END: - if in_area(pos, area): - if click: - click() - else: - return - else: - break - - default_button = { 'bg-color': ui.WHITE, 'fg-color': ui.BLACK, @@ -85,90 +41,102 @@ confirm_button_active = { } -def render_button(area, text, style): - ax, ay, aw, ah = area - tx = ax + aw // 2 - ty = ay + ah - 5 - ui.display.bar(ax, ay, aw, ah, style['bg-color']) - ui.display.text_center(tx, ty, text, - style['text-style'], - style['fg-color'], - style['bg-color']) - - -def button_widget(area, text, - click=None, - style=default_button, - active_style=default_button_active): - - def enter(): - render_button(area, text, active_style) - - def leave(): - render_button(area, text, style) - - def _click(): - leave() - click() - - render_button(area, text, style) - return click_in(area, _click, enter, leave) - - -def pin_widget(): - - pin = '' +BTN_CLICKED = const(1) + +BTN_STARTED = const(1) +BTN_ACTIVE = const(2) +BTN_DIRTY = const(4) + + +class Button(): + + def __init__(self, area, text, + style=default_button, + active_style=default_button_active): + self.area = area + self.text = text + self.style = style + self.active_style = active_style + self.state = BTN_DIRTY + + def render(self): + if not self.state & BTN_DIRTY: + return + state = self.state & ~BTN_DIRTY + style = self.active_style if state & BTN_ACTIVE else self.style + ax, ay, aw, ah = self.area + tx = ax + aw // 2 + ty = ay + ah - 5 + ui.display.bar(ax, ay, aw, ah, style['bg-color']) + ui.display.text_center(tx, ty, self.text, + style['text-style'], + style['fg-color'], + style['bg-color']) + self.state = state + + def progress(self, event, pos): + if event is loop.TOUCH_START: + if in_area(pos, self.area): + self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE + elif event is loop.TOUCH_MOVE and self.state & BTN_STARTED: + if in_area(pos, self.area): + if not self.state & BTN_ACTIVE: + self.state = BTN_STARTED | BTN_DIRTY | BTN_ACTIVE + else: + if self.state & BTN_ACTIVE: + self.state = BTN_STARTED | BTN_DIRTY + elif event is loop.TOUCH_END and self.state & BTN_STARTED: + self.state = BTN_DIRTY + if in_area(pos, self.area): + return BTN_CLICKED + + +def digit_area(d): width = const(80) height = const(60) - margin = const(5) - - def digit_area(d): - x = ((d - 1) % 3) * width - y = ((d - 1) // 3) * height - return ( - x + margin, - y + margin, - width - margin, - height - margin - ) - - def append_digit(d): - pin += str(d) - print('PIN so far: ', pin) - - def digit_widget(digit): - nonlocal pin - area = digit_area(digit) - button = button_widget( - area, - str(digit), - lambda: append_digit(digit)) - return button - - digits = (1, 2, 3, 4, 5, 6, 7, 8, 9) - buttons = [digit_widget(d) for d in digits] - - def cancel(): - raise StopIteration(None) - - def confirm(): - raise StopIteration(pin) - - cancel_widget = button_widget((0, 240 - 60, 120, 60), 'Cancel', - click=cancel, - style=cancel_button, - active_style=cancel_button_active) - confirm_widget = button_widget((120, 240 - 60, 120, 60), 'Confirm', - click=confirm, - style=confirm_button, - active_style=confirm_button_active) - - buttons.append(cancel_widget) - buttons.append(confirm_widget) - for but in buttons: - next(but) - - return buttons + margin = const(1) + x = ((d - 1) % 3) * width + y = ((d - 1) // 3) * height + return ( + x + margin, + y + margin, + width - margin, + height - margin) + + +PIN_CONFIRMED = const(1) +PIN_CANCELLED = const(2) + + +class PinDialog(): + + def __init__(self): + self.confirm_button = Button( + (0, 240-60, 120, 60), 'Confirm', + style=confirm_button, + active_style=confirm_button_active) + self.cancel_button = Button( + (120, 240-60, 120, 60), 'Cancel', + style=cancel_button, + active_style=cancel_button_active) + self.pin_buttons = [ + Button(digit_area(d), str(d)) for d in range(1, 10)] + self.pin = '' + + def render(self): + for b in self.pin_buttons: + b.render() + self.confirm_button.render() + self.cancel_button.render() + + def progress(self, event, pos): + for b in self.pin_buttons: + if b.progress(event, pos) is BTN_CLICKED: + self.pin += b.text + if self.confirm_button.progress(event, pos) is BTN_CLICKED: + return PIN_CONFIRMED + if self.cancel_button.progress(event, pos) is BTN_CLICKED: + return PIN_CANCELLED def layout_tap_to_confirm(address, amount, currency): @@ -193,17 +161,25 @@ def layout_tap_to_confirm(address, amount, currency): # animation = ui.animate_pulse(func, ui.BLACK, ui.GREY, speed=200000) - # button = button_widget((20, 20, 210, 60), 'HELLO WORLD!') - # next(button) + pin = PinDialog() - pin = pin_widget() - - result = yield loop.Wait(( - # animation, - multiplex_touch_events(pin), - )) + while True: + pin.render() - print(result) + # TODO: if we simply wait for any of scalar events, we can use + # something much more lightweight than loop.Wait + event, pos = yield loop.Wait([ + loop.TOUCH_START, + loop.TOUCH_MOVE, + loop.TOUCH_END, + ]) + result = pin.progress(event, pos) + if result is PIN_CONFIRMED: + print('PIN confirmed:', pin.pin) + return + elif result is PIN_CANCELLED: + print('PIN CANCELLED, go home') + return @unimport_func