feat(core): send ButtonRequests for paging events

pull/1538/head
matejcik 3 years ago committed by matejcik
parent 69d1465e08
commit 1012ee8497

@ -45,7 +45,13 @@ message Failure {
* @next ButtonAck
*/
message ButtonRequest {
optional ButtonRequestType code = 1;
optional ButtonRequestType code = 1; // enum identifier of the screen
optional uint32 pages = 2; // if the screen is paginated, number of pages
optional uint32 page_number = 3; // if the screen is paginated, current page (1-based)
/* Rationale: both fields are optional, and neither field can have 0 as a valid
value. So both testing `if pages` and `if page_number` do the right thing.
Also the following is always true: `page_is_last = (page_number == pages)` */
/**
* Type of button request
*/

@ -0,0 +1 @@
ButtonRequest is sent also after every screen of a multi-page view.

@ -31,7 +31,6 @@ async def confirm(
cancel_style: ButtonStyleType = Confirm.DEFAULT_CANCEL_STYLE,
major_confirm: bool = False,
) -> bool:
await button_request(ctx, code=code)
if content.__class__.__name__ == "Paginated":
# The following works because asserts are omitted in non-debug builds.
@ -46,13 +45,15 @@ async def confirm(
cancel_style,
major_confirm,
)
dialog: ui.Layout = content
result = await content.interact(ctx, code=code)
else:
await button_request(ctx, code=code)
dialog = Confirm(
content, confirm, confirm_style, cancel, cancel_style, major_confirm
)
result = await ctx.wait(dialog)
return await ctx.wait(dialog) is CONFIRMED
return result is CONFIRMED
async def info_confirm(
@ -92,21 +93,21 @@ async def hold_to_confirm(
loader_style: LoaderStyleType = HoldToConfirm.DEFAULT_LOADER_STYLE,
cancel: bool = True,
) -> bool:
await button_request(ctx, code=code)
if content.__class__.__name__ == "Paginated":
# The following works because asserts are omitted in non-debug builds.
# IOW if the assert runs, that means __debug__ is True and Paginated is imported
assert isinstance(content, Paginated)
content.pages[-1] = HoldToConfirm(
content.pages[-1], confirm, confirm_style, loader_style, cancel
)
dialog: ui.Layout = content
result = await content.interact(ctx, code=code)
else:
await button_request(ctx, code=code)
dialog = HoldToConfirm(content, confirm, confirm_style, loader_style, cancel)
result = await ctx.wait(dialog)
return await ctx.wait(dialog) is CONFIRMED
return result is CONFIRMED
async def require_confirm(*args: Any, **kwargs: Any) -> None:

@ -282,11 +282,15 @@ if TYPE_CHECKING:
class ButtonRequest(protobuf.MessageType):
code: ButtonRequestType | None
pages: int | None
page_number: int | None
def __init__(
self,
*,
code: ButtonRequestType | None = None,
pages: int | None = None,
page_number: int | None = None,
) -> None:
pass

@ -1,6 +1,8 @@
from micropython import const
from trezor import loop, res, ui, utils
from trezor import loop, res, ui, utils, wire, workflow
from trezor.enums import ButtonRequestType
from trezor.messages import ButtonAck, ButtonRequest
from .button import Button, ButtonCancel, ButtonConfirm, ButtonDefault
from .confirm import CANCELLED, CONFIRMED, Confirm
@ -9,6 +11,12 @@ from .text import TEXT_MAX_LINES, Span, Text
_PAGINATED_LINE_WIDTH = const(204)
WAS_PAGED = object()
if False:
from typing import Any
def render_scrollbar(pages: int, page: int) -> None:
BBOX = const(220)
@ -46,20 +54,19 @@ def render_swipe_text() -> None:
class Paginated(ui.Layout):
def __init__(
self, pages: list[ui.Component], page: int = 0, one_by_one: bool = False
):
def __init__(self, pages: list[ui.Component], page: int = 0):
super().__init__()
self.pages = pages
self.page = page
self.one_by_one = one_by_one
def dispatch(self, event: int, x: int, y: int) -> None:
pages = self.pages
page = self.page
pages[page].dispatch(event, x, y)
if event is ui.RENDER:
if event is ui.REPAINT:
self.repaint = True
elif event is ui.RENDER:
length = len(pages)
if page < length - 1:
render_swipe_icon()
@ -89,15 +96,24 @@ class Paginated(ui.Layout):
elif swipe is SWIPE_DOWN:
self.page = max(self.page - 1, 0)
self.pages[self.page].dispatch(ui.REPAINT, 0, 0)
self.repaint = True
if __debug__:
from apps.debug import notify_layout_change
self.on_change()
raise ui.Result(WAS_PAGED)
notify_layout_change(self)
async def interact(
self,
ctx: wire.GenericContext,
code: ButtonRequestType = ButtonRequestType.Other,
) -> Any:
workflow.close_others()
result = WAS_PAGED
while result is WAS_PAGED:
br = ButtonRequest(
code=code, pages=len(self.pages), page_number=self.page + 1
)
await ctx.call(br, ButtonAck)
result = await self
self.on_change()
return result
def create_tasks(self) -> tuple[loop.Task, ...]:
tasks: tuple[loop.Task, ...] = (
@ -118,8 +134,7 @@ class Paginated(ui.Layout):
return tasks
def on_change(self) -> None:
if self.one_by_one:
raise ui.Result(self.page)
pass
if __debug__:

@ -8,6 +8,10 @@ if False:
LayoutType = Awaitable[Any]
if __debug__:
from ..components.tt.scroll import Paginated
async def interact(
ctx: wire.GenericContext,
layout: LayoutType,
@ -15,6 +19,10 @@ async def interact(
brcode: ButtonRequestType = ButtonRequestType.Other,
) -> Any:
log.debug(__name__, "ButtonRequest.type={}".format(brtype))
workflow.close_others()
await ctx.call(ButtonRequest(code=brcode), ButtonAck)
return await ctx.wait(layout)
if layout.__class__.__name__ == "Paginated":
assert isinstance(layout, Paginated)
return await layout.interact(ctx, code=brcode)
else:
workflow.close_others()
await ctx.call(ButtonRequest(code=brcode), ButtonAck)
return await ctx.wait(layout)

@ -700,14 +700,20 @@ class ButtonRequest(protobuf.MessageType):
MESSAGE_WIRE_TYPE = 26
FIELDS = {
1: protobuf.Field("code", ButtonRequestType, repeated=False, required=False),
2: protobuf.Field("pages", "uint32", repeated=False, required=False),
3: protobuf.Field("page_number", "uint32", repeated=False, required=False),
}
def __init__(
self,
*,
code: Optional[ButtonRequestType] = None,
pages: Optional[int] = None,
page_number: Optional[int] = None,
) -> None:
self.code = code
self.pages = pages
self.page_number = page_number
class ButtonAck(protobuf.MessageType):

Loading…
Cancel
Save