From 09cd64123bd50d0ba4deea0eb225ccee7c5d2cea Mon Sep 17 00:00:00 2001 From: grdddj Date: Wed, 2 Aug 2023 12:47:33 +0200 Subject: [PATCH] feat(core): implement show_more dialogue for TR [no changelog] --- core/embed/rust/src/ui/model_tr/layout.rs | 44 ++++++++++++++++- core/embed/rust/src/ui/model_tr/theme.rs | 6 +-- core/mocks/generated/trezorui2.pyi | 11 +++++ core/src/trezor/ui/layouts/tr/__init__.py | 57 ++++++++++++++++++++--- 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 32532fc13..9ec31f2ed 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -256,7 +256,11 @@ fn content_in_button_page( // Left button - icon, text or nothing. let cancel_btn = if let Some(verb_cancel) = verb_cancel { if !verb_cancel.is_empty() { - Some(ButtonDetails::text(verb_cancel)) + if verb_cancel.as_ref() == "left_arrow_icon" { + Some(ButtonDetails::left_arrow_icon()) + } else { + Some(ButtonDetails::text(verb_cancel)) + } } else { Some(ButtonDetails::cancel_icon()) } @@ -1062,7 +1066,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu for para in IterBuf::new().try_iterate(items)? { let [font, text]: [Obj; 2] = iter_into_array(para)?; - let style: &TextStyle = theme::textstyle_number_bold_or_mono(font.try_into()?); + let style: &TextStyle = theme::textstyle_number(font.try_into()?); let text: StrBuffer = text.try_into()?; paragraphs.add(Paragraph::new(style, text)); if paragraphs.is_full() { @@ -1081,6 +1085,32 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = move |_args: &[Obj], kwargs: &Map| { + let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; + let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; + + let mut paragraphs = ParagraphVecLong::new(); + + for para in IterBuf::new().try_iterate(items)? { + let [font, text]: [Obj; 2] = iter_into_array(para)?; + let style: &TextStyle = theme::textstyle_number(font.try_into()?); + let text: StrBuffer = text.try_into()?; + paragraphs.add(Paragraph::new(style, text)); + } + + content_in_button_page( + title, + paragraphs.into_paragraphs(), + button, + Some("left_arrow_icon".into()), + false, + ) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let max_rounds: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_rounds)?.try_into()?; @@ -1648,6 +1678,16 @@ pub static mp_module_trezorui2: Module = obj_module! { /// without scrolling.""" Qstr::MP_QSTR_confirm_with_info => obj_fn_kw!(0, new_confirm_with_info).as_obj(), + /// def confirm_more( + /// *, + /// title: str, + /// button: str, + /// items: Iterable[tuple[int, str]], + /// ) -> object: + /// """Confirm long content with the possibility to go back from any page. + /// Meant to be used with confirm_with_info.""" + Qstr::MP_QSTR_confirm_more => obj_fn_kw!(0, new_confirm_more).as_obj(), + /// def confirm_coinjoin( /// *, /// max_rounds: str, diff --git a/core/embed/rust/src/ui/model_tr/theme.rs b/core/embed/rust/src/ui/model_tr/theme.rs index adbb14216..3da744ede 100644 --- a/core/embed/rust/src/ui/model_tr/theme.rs +++ b/core/embed/rust/src/ui/model_tr/theme.rs @@ -37,13 +37,13 @@ pub const TEXT_MONO_DATA: TextStyle = TEXT_MONO.with_line_breaking(LineBreaking::BreakWordsNoHyphen); /// Convert Python-side numeric id to a `TextStyle`. -/// Using only BOLD or MONO fonts. -pub fn textstyle_number_bold_or_mono(num: i32) -> &'static TextStyle { +pub fn textstyle_number(num: i32) -> &'static TextStyle { let font = Font::from_i32(-num); match font { Some(Font::BOLD) => &TEXT_BOLD, Some(Font::DEMIBOLD) => &TEXT_BOLD, - _ => &TEXT_MONO, + Some(Font::NORMAL) => &TEXT_NORMAL, + _ => &TEXT_MONO_DATA, } } diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index b7a7ce0ec..c2ff8b779 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -231,6 +231,17 @@ def confirm_with_info( without scrolling.""" +# rust/src/ui/model_tr/layout.rs +def confirm_more( + *, + title: str, + button: str, + items: Iterable[tuple[int, str]], +) -> object: + """Confirm long content with the possibility to go back from any page. + Meant to be used with confirm_with_info.""" + + # rust/src/ui/model_tr/layout.rs def confirm_coinjoin( *, diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 5e5a02e98..f776fbede 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -761,16 +761,61 @@ async def confirm_blob( extra=None, verb_cancel="", # to show the cancel icon hold=hold, + verb_cancel="", # icon ) ) - await raise_if_not_confirmed( - interact( - layout, - br_type, - br_code, + if ask_pagination and layout.page_count() > 1: + assert not hold + await _confirm_ask_pagination(br_type, title, data, description, br_code) + + else: + await raise_if_not_confirmed( + interact( + layout, + br_type, + br_code, + ) ) - ) + + +async def _confirm_ask_pagination( + br_type: str, + title: str, + data: bytes | str, + description: str, + br_code: ButtonRequestType, +) -> None: + paginated: ui.Layout | None = None + # TODO: make should_show_more/confirm_more accept bytes directly + if isinstance(data, bytes): + from ubinascii import hexlify + + data = hexlify(data).decode() + while True: + if not await should_show_more( + title, + para=[(ui.NORMAL, description), (ui.MONO, data)], + br_type=br_type, + br_code=br_code, + ): + return + + if paginated is None: + paginated = RustLayout( + trezorui2.confirm_more( + title=title, + button="GO BACK", + items=[(ui.MONO, data)], + ) + ) + else: + paginated.request_complete_repaint() + + result = await interact(paginated, br_type, br_code) + assert result in (CONFIRMED, CANCELLED) + + assert False async def confirm_address(