1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-16 04:29:08 +00:00

split ui code in modules

This commit is contained in:
Jan Pochyla 2016-05-17 18:37:26 +02:00 committed by Pavol Rusnak
parent 5968eb3f5f
commit f3c9715ff3
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
7 changed files with 250 additions and 244 deletions

View File

@ -1,29 +1,33 @@
from trezor import ui
from trezor.ui import utils as ui_utils
from trezor.ui.swipe import Swipe
from trezor import loop
def swipe_to_change_orientation():
while True:
degrees = yield from Swipe().wait()
ui.display.orientation(degrees)
def layout_homescreen():
print("Homescreen layout!")
# ui.display.bar(0, 0, 240, 240, ui.WHITE)
f = open('apps/homescreen/trezor.toig', 'rb')
def func(foreground):
f.seek(0)
ui.display.icon(0, 0, f.read(), foreground, ui.BLACK)
animation = ui.animate_pulse(func, ui.WHITE, ui.GREY, speed=400000)
ui.display.icon(0, 0, f.read(), foreground, ui_utils.BLACK)
orientation = swipe_to_change_orientation()
animation = ui_utils.animate_pulse(func, ui_utils.WHITE, ui_utils.GREY, speed=400000)
timeout = loop.Sleep(5000 * 1000)
yield loop.Wait([animation, timeout])
print('back to layout')
# try:
# print(animation.throw(StopIteration()))
# except:
# pass
yield loop.Wait([
orientation,
animation,
timeout
])
from apps import playground
return playground.layout_tap_to_confirm('1BitkeyP2nDd5oa64x7AjvBbbwST54W5Zmx2', 110.126967, 'BTC')

View File

@ -1,222 +1,10 @@
import utime
from trezor import loop
from trezor import ui
from trezor.ui.swipe import Swipe
from trezor.ui.pin import PinDialog, PIN_CONFIRMED, PIN_CANCELLED
from trezor.utils import unimport_func
def in_area(pos, area):
x, y = pos
ax, ay, aw, ah = area
return ax <= x <= ax + aw and ay <= y <= ay + ah
DEFAULT_BUTTON = {
'bg-color': ui.BLACK,
'fg-color': ui.WHITE,
'text-style': ui.NORMAL,
'border-color': ui.blend(ui.BLACK, ui.WHITE, 0.1),
}
DEFAULT_BUTTON_ACTIVE = {
'bg-color': ui.GREY,
'fg-color': ui.BLACK,
'text-style': ui.BOLD,
'border-color': ui.GREY,
}
CANCEL_BUTTON = {
'bg-color': ui.blend(ui.BLACK, ui.RED, 0.3),
'fg-color': ui.RED,
'text-style': ui.NORMAL,
'border-color': ui.blend(ui.BLACK, ui.RED, 0.6),
}
CANCEL_BUTTON_ACTIVE = {
'bg-color': ui.RED,
'fg-color': ui.WHITE,
'text-style': ui.BOLD,
'border-color': ui.RED,
}
CONFIRM_BUTTON = {
'bg-color': ui.blend(ui.BLACK, ui.GREEN, 0.3),
'fg-color': ui.GREEN,
'text-style': ui.NORMAL,
'border-color': ui.blend(ui.BLACK, ui.GREEN, 0.6),
}
CONFIRM_BUTTON_ACTIVE = {
'bg-color': ui.GREEN,
'fg-color': ui.WHITE,
'text-style': ui.BOLD,
'border-color': ui.GREEN,
}
BTN_CLICKED = const(1)
BTN_STARTED = const(1)
BTN_ACTIVE = const(2)
BTN_DIRTY = const(4)
class Button():
def __init__(self, area, text, normal_style=None, active_style=None):
self.area = area
self.text = text
self.normal_style = normal_style or DEFAULT_BUTTON
self.active_style = active_style or DEFAULT_BUTTON_ACTIVE
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.normal_style
ax, ay, aw, ah = self.area
tx = ax + aw // 2
ty = ay + ah // 2 + 8
ui.display.bar(ax, ay, aw, ah, style['border-color'])
ui.display.bar(ax + 1, ay + 1, aw - 2, ah - 2, style['bg-color'])
ui.display.text_center(tx, ty, self.text,
style['text-style'],
style['fg-color'],
style['bg-color'])
self.state = state
def send(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)
x = ((d - 1) % 3) * width
y = ((d - 1) // 3) * height
return (x, y, width, height)
PIN_CONFIRMED = const(1)
PIN_CANCELLED = const(2)
class PinDialog():
def __init__(self, pin=''):
self.pin = pin
self.confirm_button = Button((0, 240 - 60, 120, 60), 'Confirm',
normal_style=CONFIRM_BUTTON,
active_style=CONFIRM_BUTTON_ACTIVE)
self.cancel_button = Button((120, 240 - 60, 120, 60), 'Cancel',
normal_style=CANCEL_BUTTON,
active_style=CANCEL_BUTTON_ACTIVE)
self.pin_buttons = [Button(digit_area(dig), str(dig))
for dig in range(1, 10)]
def render(self):
for btn in self.pin_buttons:
btn.render()
self.confirm_button.render()
self.cancel_button.render()
def send(self, event, pos):
for btn in self.pin_buttons:
if btn.send(event, pos) is BTN_CLICKED:
self.pin += btn.text
if self.confirm_button.send(event, pos) is BTN_CLICKED:
return PIN_CONFIRMED
if self.cancel_button.send(event, pos) is BTN_CLICKED:
return PIN_CANCELLED
def wait_for_result(self):
while True:
self.render()
event, *pos = yield loop.Select(loop.TOUCH_START,
loop.TOUCH_MOVE,
loop.TOUCH_END)
result = self.send(event, pos)
if result is not None:
return result
SWIPE_DISTANCE_THRESHOLD = const(20) # Min pixels in the primary direction
SWIPE_VELOCITY_THRESHOLD = const(200) # Min pixels/second
SWIPE_RATIO_THRESHOLD = const(30) # Max ratio secondary to primary direction in %
SWIPE_UP = const(180)
SWIPE_DOWN = const(0)
SWIPE_LEFT = const(90)
SWIPE_RIGHT = const(270)
class Swipe():
def __init__(self, area=None):
self.area = area or (0, 0, 240, 240)
self.start_pos = None
self.start_time = 0
self.end_pos = None
self.end_time = 0
def send(self, event, pos):
if event is loop.TOUCH_START and in_area(pos, self.area):
self.start_time = utime.time()
self.start_pos = pos
elif event is loop.TOUCH_END and self.start_pos is not None:
self.end_time = utime.time()
self.end_pos = pos
td = self.end_time - self.start_time
pdx = self.end_pos[0] - self.start_pos[0]
pdy = self.end_pos[1] - self.start_pos[1]
pdxa = abs(pdx)
pdya = abs(pdy)
if pdxa > pdya:
# Horizontal direction
velx = pdx / td
velxa = abs(velx)
ratio = int(pdya / pdxa * 100) if pdxa > 0 else 100
print('velxa', velxa, 'pdxa', pdxa, 'ratio', ratio)
if (velxa >= SWIPE_VELOCITY_THRESHOLD
and pdxa >= SWIPE_DISTANCE_THRESHOLD
and ratio <= SWIPE_RATIO_THRESHOLD):
return SWIPE_RIGHT if pdx > 0 else SWIPE_LEFT
else:
# Vertical direction
vely = pdy / td
velya = abs(vely)
ratio = int(pdxa / pdya * 100) if pdya > 0 else 100
print('velya', velya, 'pdya', pdya, 'ratio', ratio)
if (velya >= SWIPE_VELOCITY_THRESHOLD
and pdya >= SWIPE_DISTANCE_THRESHOLD
and ratio <= SWIPE_RATIO_THRESHOLD):
return SWIPE_DOWN if pdy > 0 else SWIPE_UP
# No swipe, reset the state
self.start_pos = None
self.start_time = 0
self.end_pos = None
self.end_time = 0
def wait(self):
while True:
event, *pos = yield loop.Select(loop.TOUCH_START, loop.TOUCH_END)
result = self.send(event, pos)
if result is not None:
return result
def layout_tap_to_confirm(address, amount, currency):
# ui.display.bar(0, 0, 240, 40, ui.GREEN)
@ -239,22 +27,14 @@ def layout_tap_to_confirm(address, amount, currency):
# animation = ui.animate_pulse(func, ui.BLACK, ui.GREY, speed=200000)
# pin_dialog = PinDialog()
# pin_result = yield from pin_dialog.wait_for_result()
pin_dialog = PinDialog()
pin_result = yield from pin_dialog.wait_for_result()
# if pin_result is PIN_CONFIRMED:
# print('PIN confirmed:', pin_dialog.pin)
if pin_result is PIN_CONFIRMED:
print('PIN confirmed:', pin_dialog.pin)
# elif pin_result is PIN_CANCELLED:
# print('PIN CANCELLED, go home')
degrees = 0
while True:
ui.display.bar(0, 0, 240, 240, ui.BLACK)
ui.display.text_center(120, 130, 'HELLO WORLD!', ui.NORMAL, ui.WHITE, ui.BLACK)
degrees = yield from Swipe().wait()
ui.display.orientation(degrees)
elif pin_result is PIN_CANCELLED:
print('PIN CANCELLED, go home')
@unimport_func

View File

@ -0,0 +1,3 @@
from TrezorUi import Display
display = Display()

94
src/trezor/ui/button.py Normal file
View File

@ -0,0 +1,94 @@
from . import utils
from . import display
from .utils import in_area
from trezor import loop
DEFAULT_BUTTON = {
'bg-color': utils.BLACK,
'fg-color': utils.WHITE,
'text-style': utils.NORMAL,
'border-color': utils.blend(utils.BLACK, utils.WHITE, 0.1),
}
DEFAULT_BUTTON_ACTIVE = {
'bg-color': utils.GREY,
'fg-color': utils.BLACK,
'text-style': utils.BOLD,
'border-color': utils.GREY,
}
CANCEL_BUTTON = {
'bg-color': utils.blend(utils.BLACK, utils.RED, 0.3),
'fg-color': utils.RED,
'text-style': utils.NORMAL,
'border-color': utils.blend(utils.BLACK, utils.RED, 0.6),
}
CANCEL_BUTTON_ACTIVE = {
'bg-color': utils.RED,
'fg-color': utils.WHITE,
'text-style': utils.BOLD,
'border-color': utils.RED,
}
CONFIRM_BUTTON = {
'bg-color': utils.blend(utils.BLACK, utils.GREEN, 0.3),
'fg-color': utils.GREEN,
'text-style': utils.NORMAL,
'border-color': utils.blend(utils.BLACK, utils.GREEN, 0.6),
}
CONFIRM_BUTTON_ACTIVE = {
'bg-color': utils.GREEN,
'fg-color': utils.WHITE,
'text-style': utils.BOLD,
'border-color': utils.GREEN,
}
BTN_CLICKED = const(1)
BTN_STARTED = const(1)
BTN_ACTIVE = const(2)
BTN_DIRTY = const(4)
class Button():
def __init__(self, area, text, normal_style=None, active_style=None):
self.area = area
self.text = text
self.normal_style = normal_style or DEFAULT_BUTTON
self.active_style = active_style or DEFAULT_BUTTON_ACTIVE
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.normal_style
ax, ay, aw, ah = self.area
tx = ax + aw // 2
ty = ay + ah // 2 + 8
display.bar(ax, ay, aw, ah, style['border-color'])
display.bar(ax + 1, ay + 1, aw - 2, ah - 2, style['bg-color'])
display.text_center(tx, ty, self.text,
style['text-style'],
style['fg-color'],
style['bg-color'])
self.state = state
def send(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

53
src/trezor/ui/pin.py Normal file
View File

@ -0,0 +1,53 @@
from . import button
from trezor import loop
def digit_area(d):
width = const(80)
height = const(60)
x = ((d - 1) % 3) * width
y = ((d - 1) // 3) * height
return (x, y, width, height)
PIN_CONFIRMED = const(1)
PIN_CANCELLED = const(2)
class PinDialog():
def __init__(self, pin=''):
self.pin = pin
self.confirm_button = button.Button((0, 240 - 60, 120, 60), 'Confirm',
normal_style=button.CONFIRM_BUTTON,
active_style=button.CONFIRM_BUTTON_ACTIVE)
self.cancel_button = button.Button((120, 240 - 60, 120, 60), 'Cancel',
normal_style=button.CANCEL_BUTTON,
active_style=button.CANCEL_BUTTON_ACTIVE)
self.pin_buttons = [button.Button(digit_area(dig), str(dig))
for dig in range(1, 10)]
def render(self):
for btn in self.pin_buttons:
btn.render()
self.confirm_button.render()
self.cancel_button.render()
def send(self, event, pos):
for btn in self.pin_buttons:
if btn.send(event, pos) is button.BTN_CLICKED:
self.pin += btn.text
if self.confirm_button.send(event, pos) is button.BTN_CLICKED:
return PIN_CONFIRMED
if self.cancel_button.send(event, pos) is button.BTN_CLICKED:
return PIN_CANCELLED
def wait_for_result(self):
while True:
self.render()
event, *pos = yield loop.Select(loop.TOUCH_START,
loop.TOUCH_MOVE,
loop.TOUCH_END)
result = self.send(event, pos)
if result is not None:
return result

67
src/trezor/ui/swipe.py Normal file
View File

@ -0,0 +1,67 @@
import utime
from .utils import in_area
from trezor import loop
SWIPE_DISTANCE_THRESHOLD = const(20) # Min pixels in the primary direction
SWIPE_VELOCITY_THRESHOLD = const(200) # Min pixels/second
SWIPE_RATIO_THRESHOLD = const(30) # Max ratio secondary to primary direction in %
SWIPE_UP = const(180)
SWIPE_DOWN = const(0)
SWIPE_LEFT = const(90)
SWIPE_RIGHT = const(270)
class Swipe():
def __init__(self, area=None):
self.area = area or (0, 0, 240, 240)
self.start_pos = None
self.start_time = 0
self.end_pos = None
self.end_time = 0
def send(self, event, pos):
if event is loop.TOUCH_START and in_area(pos, self.area):
self.start_time = utime.time()
self.start_pos = pos
elif event is loop.TOUCH_END and self.start_pos is not None:
self.end_time = utime.time()
self.end_pos = pos
td = self.end_time - self.start_time
pdx = self.end_pos[0] - self.start_pos[0]
pdy = self.end_pos[1] - self.start_pos[1]
pdxa = abs(pdx)
pdya = abs(pdy)
if pdxa > pdya:
# Horizontal direction
velxa = abs(pdx / td)
ratio = int(pdya / pdxa * 100) if pdxa > 0 else 100
if (velxa >= SWIPE_VELOCITY_THRESHOLD
and pdxa >= SWIPE_DISTANCE_THRESHOLD
and ratio <= SWIPE_RATIO_THRESHOLD):
return SWIPE_RIGHT if pdx > 0 else SWIPE_LEFT
else:
# Vertical direction
velya = abs(pdy / td)
ratio = int(pdxa / pdya * 100) if pdya > 0 else 100
if (velya >= SWIPE_VELOCITY_THRESHOLD
and pdya >= SWIPE_DISTANCE_THRESHOLD
and ratio <= SWIPE_RATIO_THRESHOLD):
return SWIPE_DOWN if pdy > 0 else SWIPE_UP
# No swipe, reset the state
self.start_pos = None
self.start_time = 0
self.end_pos = None
self.end_time = 0
def wait(self):
while True:
event, *pos = yield loop.Select(loop.TOUCH_START, loop.TOUCH_END)
result = self.send(event, pos)
if result is not None:
return result

View File

@ -1,15 +1,13 @@
import math
import utime
from TrezorUi import Display
from trezor import loop
from . import loop
display = Display()
def rgbcolor(r: int, g: int, b: int) -> int:
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3)
RED = rgbcolor(0xF4, 0x43, 0x36)
PINK = rgbcolor(0xE9, 0x1E, 0x63)
PURPLE = rgbcolor(0x9C, 0x27, 0xB0)
@ -36,6 +34,13 @@ MONO = const(0)
NORMAL = const(1)
BOLD = const(2)
def in_area(pos, area):
x, y = pos
ax, ay, aw, ah = area
return ax <= x <= ax + aw and ay <= y <= ay + ah
def lerpi(a: int, b: int, t: float) -> int:
return int(a + t * (b - a))