mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-25 14:50:57 +00:00
core/ui: Implement multi-page confirmation screen.
This commit is contained in:
parent
1f58ee7ae9
commit
509a815f73
BIN
core/src/trezor/res/swipe_left.toif
Normal file
BIN
core/src/trezor/res/swipe_left.toif
Normal file
Binary file not shown.
BIN
core/src/trezor/res/swipe_right.toif
Normal file
BIN
core/src/trezor/res/swipe_right.toif
Normal file
Binary file not shown.
@ -6,7 +6,7 @@ from trezorui import Display
|
|||||||
from trezor import io, loop, res, utils
|
from trezor import io, loop, res, utils
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Any, Generator, Iterable, Tuple, TypeVar
|
from typing import Any, Generator, Tuple, TypeVar
|
||||||
|
|
||||||
Pos = Tuple[int, int]
|
Pos = Tuple[int, int]
|
||||||
Area = Tuple[int, int, int, int]
|
Area = Tuple[int, int, int, int]
|
||||||
@ -276,7 +276,7 @@ class Layout(Component):
|
|||||||
def __await__(self) -> Generator[Any, Any, ResultValue]:
|
def __await__(self) -> Generator[Any, Any, ResultValue]:
|
||||||
return self.__iter__() # type: ignore
|
return self.__iter__() # type: ignore
|
||||||
|
|
||||||
def create_tasks(self) -> Iterable[loop.Task]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
"""
|
"""
|
||||||
Called from `__iter__`. Creates and returns a sequence of tasks that
|
Called from `__iter__`. Creates and returns a sequence of tasks that
|
||||||
run this layout. Tasks are executed in parallel. When one of them
|
run this layout. Tasks are executed in parallel. When one of them
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from trezor import res, ui
|
from micropython import const
|
||||||
|
|
||||||
|
from trezor import loop, res, ui
|
||||||
from trezor.ui.button import Button, ButtonCancel, ButtonConfirm
|
from trezor.ui.button import Button, ButtonCancel, ButtonConfirm
|
||||||
from trezor.ui.loader import Loader, LoaderDefault
|
from trezor.ui.loader import Loader, LoaderDefault
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Optional
|
from typing import Any, Optional, Tuple
|
||||||
from trezor.ui.button import ButtonContent, ButtonStyleType
|
from trezor.ui.button import ButtonContent, ButtonStyleType
|
||||||
from trezor.ui.loader import LoaderStyleType
|
from trezor.ui.loader import LoaderStyleType
|
||||||
|
|
||||||
@ -55,6 +57,7 @@ class Confirm(ui.Layout):
|
|||||||
self.cancel = None
|
self.cancel = None
|
||||||
|
|
||||||
def dispatch(self, event: int, x: int, y: int) -> None:
|
def dispatch(self, event: int, x: int, y: int) -> None:
|
||||||
|
super().dispatch(event, x, y)
|
||||||
self.content.dispatch(event, x, y)
|
self.content.dispatch(event, x, y)
|
||||||
if self.confirm is not None:
|
if self.confirm is not None:
|
||||||
self.confirm.dispatch(event, x, y)
|
self.confirm.dispatch(event, x, y)
|
||||||
@ -68,6 +71,82 @@ class Confirm(ui.Layout):
|
|||||||
raise ui.Result(CANCELLED)
|
raise ui.Result(CANCELLED)
|
||||||
|
|
||||||
|
|
||||||
|
class Pageable:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._page = 0
|
||||||
|
|
||||||
|
def page(self) -> int:
|
||||||
|
return self._page
|
||||||
|
|
||||||
|
def page_count(self) -> int:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def is_first(self) -> bool:
|
||||||
|
return self._page == 0
|
||||||
|
|
||||||
|
def is_last(self) -> bool:
|
||||||
|
return self._page == self.page_count() - 1
|
||||||
|
|
||||||
|
def next(self) -> None:
|
||||||
|
self._page = min(self._page + 1, self.page_count() - 1)
|
||||||
|
|
||||||
|
def prev(self) -> None:
|
||||||
|
self._page = max(self._page - 1, 0)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmPageable(Confirm):
|
||||||
|
def __init__(self, pageable: Pageable, *args: Any, **kwargs: Any):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.pageable = pageable
|
||||||
|
|
||||||
|
async def handle_paging(self) -> None:
|
||||||
|
from trezor.ui.swipe import SWIPE_HORIZONTAL, SWIPE_LEFT, SWIPE_RIGHT, Swipe
|
||||||
|
|
||||||
|
if self.pageable.is_first():
|
||||||
|
directions = SWIPE_LEFT
|
||||||
|
elif self.pageable.is_last():
|
||||||
|
directions = SWIPE_RIGHT
|
||||||
|
else:
|
||||||
|
directions = SWIPE_HORIZONTAL
|
||||||
|
|
||||||
|
swipe = await Swipe(directions)
|
||||||
|
|
||||||
|
if swipe == SWIPE_LEFT:
|
||||||
|
self.pageable.next()
|
||||||
|
else:
|
||||||
|
self.pageable.prev()
|
||||||
|
|
||||||
|
self.content.repaint = True
|
||||||
|
if self.confirm is not None:
|
||||||
|
self.confirm.repaint = True
|
||||||
|
if self.cancel is not None:
|
||||||
|
self.cancel.repaint = True
|
||||||
|
|
||||||
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
|
tasks = super().create_tasks()
|
||||||
|
if self.pageable.page_count() > 1:
|
||||||
|
return tasks + (self.handle_paging(),)
|
||||||
|
else:
|
||||||
|
return tasks
|
||||||
|
|
||||||
|
def on_render(self) -> None:
|
||||||
|
PULSE_PERIOD = const(1200000)
|
||||||
|
|
||||||
|
super().on_render()
|
||||||
|
|
||||||
|
if not self.pageable.is_first():
|
||||||
|
t = ui.pulse(PULSE_PERIOD)
|
||||||
|
c = ui.blend(ui.GREY, ui.DARK_GREY, t)
|
||||||
|
icon = res.load(ui.ICON_SWIPE_RIGHT)
|
||||||
|
ui.display.icon(18, 68, icon, c, ui.BG)
|
||||||
|
|
||||||
|
if not self.pageable.is_last():
|
||||||
|
t = ui.pulse(PULSE_PERIOD, PULSE_PERIOD // 2)
|
||||||
|
c = ui.blend(ui.GREY, ui.DARK_GREY, t)
|
||||||
|
icon = res.load(ui.ICON_SWIPE_LEFT)
|
||||||
|
ui.display.icon(205, 68, icon, c, ui.BG)
|
||||||
|
|
||||||
|
|
||||||
class HoldToConfirm(ui.Layout):
|
class HoldToConfirm(ui.Layout):
|
||||||
DEFAULT_CONFIRM = "Hold To Confirm"
|
DEFAULT_CONFIRM = "Hold To Confirm"
|
||||||
DEFAULT_CONFIRM_STYLE = ButtonConfirm
|
DEFAULT_CONFIRM_STYLE = ButtonConfirm
|
||||||
|
@ -7,7 +7,7 @@ from trezor.ui.button import Button, ButtonClear, ButtonConfirm
|
|||||||
from trezor.ui.swipe import SWIPE_HORIZONTAL, SWIPE_LEFT, Swipe
|
from trezor.ui.swipe import SWIPE_HORIZONTAL, SWIPE_LEFT, Swipe
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import List, Iterable, Optional
|
from typing import Iterable, List, Optional, Tuple
|
||||||
from trezor.ui.button import ButtonContent, ButtonStyleStateType
|
from trezor.ui.button import ButtonContent, ButtonStyleStateType
|
||||||
|
|
||||||
SPACE = res.load(ui.ICON_SPACE)
|
SPACE = res.load(ui.ICON_SPACE)
|
||||||
@ -244,7 +244,7 @@ class PassphraseKeyboard(ui.Layout):
|
|||||||
def on_confirm(self) -> None:
|
def on_confirm(self) -> None:
|
||||||
raise ui.Result(self.input.text)
|
raise ui.Result(self.input.text)
|
||||||
|
|
||||||
def create_tasks(self) -> Iterable[loop.Task]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
return self.handle_input(), self.handle_rendering(), self.handle_paging()
|
return self.handle_input(), self.handle_rendering(), self.handle_paging()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from trezor import loop, ui
|
from trezor import loop, ui
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Iterable
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
class Popup(ui.Layout):
|
class Popup(ui.Layout):
|
||||||
@ -12,7 +12,7 @@ class Popup(ui.Layout):
|
|||||||
def dispatch(self, event: int, x: int, y: int) -> None:
|
def dispatch(self, event: int, x: int, y: int) -> None:
|
||||||
self.content.dispatch(event, x, y)
|
self.content.dispatch(event, x, y)
|
||||||
|
|
||||||
def create_tasks(self) -> Iterable[loop.Task]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
return self.handle_input(), self.handle_rendering(), self.handle_timeout()
|
return self.handle_input(), self.handle_rendering(), self.handle_timeout()
|
||||||
|
|
||||||
def handle_timeout(self) -> loop.Task: # type: ignore
|
def handle_timeout(self) -> loop.Task: # type: ignore
|
||||||
|
@ -9,7 +9,7 @@ if __debug__:
|
|||||||
from apps.debug import swipe_signal
|
from apps.debug import swipe_signal
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import Iterable, Sequence
|
from typing import Tuple, Sequence
|
||||||
|
|
||||||
|
|
||||||
def render_scrollbar(pages: int, page: int) -> None:
|
def render_scrollbar(pages: int, page: int) -> None:
|
||||||
@ -91,7 +91,7 @@ class Paginated(ui.Layout):
|
|||||||
|
|
||||||
self.on_change()
|
self.on_change()
|
||||||
|
|
||||||
def create_tasks(self) -> Iterable[loop.Task]:
|
def create_tasks(self) -> Tuple[loop.Task, ...]:
|
||||||
return self.handle_input(), self.handle_rendering(), self.handle_paging()
|
return self.handle_input(), self.handle_rendering(), self.handle_paging()
|
||||||
|
|
||||||
def on_change(self) -> None:
|
def on_change(self) -> None:
|
||||||
|
@ -64,5 +64,7 @@ ICON_LOCK = "trezor/res/lock.toif"
|
|||||||
ICON_CLICK = "trezor/res/click.toif"
|
ICON_CLICK = "trezor/res/click.toif"
|
||||||
ICON_BACK = "trezor/res/left.toif"
|
ICON_BACK = "trezor/res/left.toif"
|
||||||
ICON_SWIPE = "trezor/res/swipe.toif"
|
ICON_SWIPE = "trezor/res/swipe.toif"
|
||||||
|
ICON_SWIPE_LEFT = "trezor/res/swipe_left.toif"
|
||||||
|
ICON_SWIPE_RIGHT = "trezor/res/swipe_right.toif"
|
||||||
ICON_CHECK = "trezor/res/check.toif"
|
ICON_CHECK = "trezor/res/check.toif"
|
||||||
ICON_SPACE = "trezor/res/space.toif"
|
ICON_SPACE = "trezor/res/space.toif"
|
||||||
|
Loading…
Reference in New Issue
Block a user