mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +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
|
* @next ButtonAck
|
||||||
*/
|
*/
|
||||||
message ButtonRequest {
|
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
|
* 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,
|
cancel_style: ButtonStyleType = Confirm.DEFAULT_CANCEL_STYLE,
|
||||||
major_confirm: bool = False,
|
major_confirm: bool = False,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
await button_request(ctx, code=code)
|
|
||||||
|
|
||||||
if content.__class__.__name__ == "Paginated":
|
if content.__class__.__name__ == "Paginated":
|
||||||
# The following works because asserts are omitted in non-debug builds.
|
# The following works because asserts are omitted in non-debug builds.
|
||||||
@ -46,13 +45,15 @@ async def confirm(
|
|||||||
cancel_style,
|
cancel_style,
|
||||||
major_confirm,
|
major_confirm,
|
||||||
)
|
)
|
||||||
dialog: ui.Layout = content
|
result = await content.interact(ctx, code=code)
|
||||||
else:
|
else:
|
||||||
|
await button_request(ctx, code=code)
|
||||||
dialog = Confirm(
|
dialog = Confirm(
|
||||||
content, confirm, confirm_style, cancel, cancel_style, major_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(
|
async def info_confirm(
|
||||||
@ -92,21 +93,21 @@ async def hold_to_confirm(
|
|||||||
loader_style: LoaderStyleType = HoldToConfirm.DEFAULT_LOADER_STYLE,
|
loader_style: LoaderStyleType = HoldToConfirm.DEFAULT_LOADER_STYLE,
|
||||||
cancel: bool = True,
|
cancel: bool = True,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
await button_request(ctx, code=code)
|
|
||||||
|
|
||||||
if content.__class__.__name__ == "Paginated":
|
if content.__class__.__name__ == "Paginated":
|
||||||
# The following works because asserts are omitted in non-debug builds.
|
# 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
|
# IOW if the assert runs, that means __debug__ is True and Paginated is imported
|
||||||
assert isinstance(content, Paginated)
|
assert isinstance(content, Paginated)
|
||||||
|
|
||||||
content.pages[-1] = HoldToConfirm(
|
content.pages[-1] = HoldToConfirm(
|
||||||
content.pages[-1], confirm, confirm_style, loader_style, cancel
|
content.pages[-1], confirm, confirm_style, loader_style, cancel
|
||||||
)
|
)
|
||||||
dialog: ui.Layout = content
|
result = await content.interact(ctx, code=code)
|
||||||
else:
|
else:
|
||||||
|
await button_request(ctx, code=code)
|
||||||
dialog = HoldToConfirm(content, confirm, confirm_style, loader_style, cancel)
|
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:
|
async def require_confirm(*args: Any, **kwargs: Any) -> None:
|
||||||
|
@ -282,11 +282,15 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
class ButtonRequest(protobuf.MessageType):
|
class ButtonRequest(protobuf.MessageType):
|
||||||
code: ButtonRequestType | None
|
code: ButtonRequestType | None
|
||||||
|
pages: int | None
|
||||||
|
page_number: int | None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
code: ButtonRequestType | None = None,
|
code: ButtonRequestType | None = None,
|
||||||
|
pages: int | None = None,
|
||||||
|
page_number: int | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from micropython import const
|
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 .button import Button, ButtonCancel, ButtonConfirm, ButtonDefault
|
||||||
from .confirm import CANCELLED, CONFIRMED, Confirm
|
from .confirm import CANCELLED, CONFIRMED, Confirm
|
||||||
@ -9,6 +11,12 @@ from .text import TEXT_MAX_LINES, Span, Text
|
|||||||
|
|
||||||
_PAGINATED_LINE_WIDTH = const(204)
|
_PAGINATED_LINE_WIDTH = const(204)
|
||||||
|
|
||||||
|
WAS_PAGED = object()
|
||||||
|
|
||||||
|
|
||||||
|
if False:
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def render_scrollbar(pages: int, page: int) -> None:
|
def render_scrollbar(pages: int, page: int) -> None:
|
||||||
BBOX = const(220)
|
BBOX = const(220)
|
||||||
@ -46,20 +54,19 @@ def render_swipe_text() -> None:
|
|||||||
|
|
||||||
|
|
||||||
class Paginated(ui.Layout):
|
class Paginated(ui.Layout):
|
||||||
def __init__(
|
def __init__(self, pages: list[ui.Component], page: int = 0):
|
||||||
self, pages: list[ui.Component], page: int = 0, one_by_one: bool = False
|
|
||||||
):
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.pages = pages
|
self.pages = pages
|
||||||
self.page = page
|
self.page = page
|
||||||
self.one_by_one = one_by_one
|
|
||||||
|
|
||||||
def dispatch(self, event: int, x: int, y: int) -> None:
|
def dispatch(self, event: int, x: int, y: int) -> None:
|
||||||
pages = self.pages
|
pages = self.pages
|
||||||
page = self.page
|
page = self.page
|
||||||
pages[page].dispatch(event, x, y)
|
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)
|
length = len(pages)
|
||||||
if page < length - 1:
|
if page < length - 1:
|
||||||
render_swipe_icon()
|
render_swipe_icon()
|
||||||
@ -89,15 +96,24 @@ class Paginated(ui.Layout):
|
|||||||
elif swipe is SWIPE_DOWN:
|
elif swipe is SWIPE_DOWN:
|
||||||
self.page = max(self.page - 1, 0)
|
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()
|
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, ...]:
|
def create_tasks(self) -> tuple[loop.Task, ...]:
|
||||||
tasks: tuple[loop.Task, ...] = (
|
tasks: tuple[loop.Task, ...] = (
|
||||||
@ -118,8 +134,7 @@ class Paginated(ui.Layout):
|
|||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
def on_change(self) -> None:
|
def on_change(self) -> None:
|
||||||
if self.one_by_one:
|
pass
|
||||||
raise ui.Result(self.page)
|
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ if False:
|
|||||||
LayoutType = Awaitable[Any]
|
LayoutType = Awaitable[Any]
|
||||||
|
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
from ..components.tt.scroll import Paginated
|
||||||
|
|
||||||
|
|
||||||
async def interact(
|
async def interact(
|
||||||
ctx: wire.GenericContext,
|
ctx: wire.GenericContext,
|
||||||
layout: LayoutType,
|
layout: LayoutType,
|
||||||
@ -15,6 +19,10 @@ async def interact(
|
|||||||
brcode: ButtonRequestType = ButtonRequestType.Other,
|
brcode: ButtonRequestType = ButtonRequestType.Other,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
log.debug(__name__, "ButtonRequest.type={}".format(brtype))
|
log.debug(__name__, "ButtonRequest.type={}".format(brtype))
|
||||||
workflow.close_others()
|
if layout.__class__.__name__ == "Paginated":
|
||||||
await ctx.call(ButtonRequest(code=brcode), ButtonAck)
|
assert isinstance(layout, Paginated)
|
||||||
return await ctx.wait(layout)
|
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
|
MESSAGE_WIRE_TYPE = 26
|
||||||
FIELDS = {
|
FIELDS = {
|
||||||
1: protobuf.Field("code", ButtonRequestType, repeated=False, required=False),
|
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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
code: Optional[ButtonRequestType] = None,
|
code: Optional[ButtonRequestType] = None,
|
||||||
|
pages: Optional[int] = None,
|
||||||
|
page_number: Optional[int] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.code = code
|
self.code = code
|
||||||
|
self.pages = pages
|
||||||
|
self.page_number = page_number
|
||||||
|
|
||||||
|
|
||||||
class ButtonAck(protobuf.MessageType):
|
class ButtonAck(protobuf.MessageType):
|
||||||
|
Loading…
Reference in New Issue
Block a user