mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 07:28:10 +00:00
feat(core): send ButtonRequests for paging events
This commit is contained in:
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
|
||||
*/
|
||||
|
1
core/.changelog.d/1671.added
Normal file
1
core/.changelog.d/1671.added
Normal file
@ -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
|
||||
|
||||
notify_layout_change(self)
|
||||
|
||||
self.on_change()
|
||||
raise ui.Result(WAS_PAGED)
|
||||
|
||||
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
|
||||
|
||||
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…
Reference in New Issue
Block a user