diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 2ce8dcae51..801cbb6c96 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -4,21 +4,28 @@ use heapless::Vec; use crate::{ error::Error, - micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util}, + micropython::{ + buffer::StrBuffer, + iter::{Iter, IterBuf}, + map::Map, + module::Module, + obj::Obj, + qstr::Qstr, + util, + }, time::Duration, ui::{ component::{ base::{Component, ComponentExt}, paginated::{PageMsg, Paginate}, - text::paragraphs::{Paragraph, Paragraphs}, + text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecLong, Paragraphs, VecExt}, FormattedText, }, display::Font, layout::{ obj::{ComponentMsgObj, LayoutObj}, result::{CANCELLED, CONFIRMED, INFO}, - util::iter_into_vec, - util::upy_disable_animation, + util::{iter_into_objs, iter_into_vec, upy_disable_animation}, }, model_tr::component::LineAlignment, }, @@ -192,6 +199,45 @@ extern "C" fn new_confirm_text(n_args: usize, args: *const Obj, kwargs: *mut Map unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn confirm_properties(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 hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?; + let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; + + let mut paragraphs = ParagraphVecLong::new(); + + let mut iter_buf = IterBuf::new(); + let iter = Iter::try_from_obj_with_buf(items, &mut iter_buf)?; + for para in iter { + let [key, value, _is_mono]: [Obj; 3] = iter_into_objs(para)?; + let key = key.try_into_option::()?; + let value = value.try_into_option::()?; + + if let Some(key) = key { + if value.is_some() { + paragraphs.add(Paragraph::new(&theme::TEXT_BOLD, key).no_break()); + } else { + paragraphs.add(Paragraph::new(&theme::TEXT_BOLD, key)); + } + } + if let Some(value) = value { + paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value)); + } + } + + let mut content = ButtonPage::new_str(paragraphs.into_paragraphs(), theme::BG); + if hold { + let confirm_btn = + Some(ButtonDetails::text("CONFIRM").with_duration(Duration::from_secs(1))); + content = content.with_confirm_btn(confirm_btn); + } + let obj = LayoutObj::new(Frame::new(title, content))?; + Ok(obj.into()) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = |_args: &[Obj], kwargs: &Map| { let address: StrBuffer = kwargs.get(Qstr::MP_QSTR_address)?.try_into()?; @@ -524,6 +570,17 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm action.""" Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(), + /// def confirm_properties( + /// *, + /// title: str, + /// items: Iterable[Tuple[str | None, str | None, bool]], + /// hold: bool = False, + /// ) -> object: + /// """Confirm list of key-value pairs. The third component in the tuple should be True if + /// the value is to be rendered as binary with monospace font, False otherwise. + /// This only concerns the text style, you need to decode the value to UTF-8 in python.""" + Qstr::MP_QSTR_confirm_properties => obj_fn_kw!(0, confirm_properties).as_obj(), + /// def confirm_output_r( /// *, /// address: str, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 3b7d72eb46..670226a962 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -55,6 +55,18 @@ def confirm_action( """Confirm action.""" +# rust/src/ui/model_tr/layout.rs +def confirm_properties( + *, + title: str, + items: Iterable[Tuple[str | None, str | None, bool]], + hold: bool = False, +) -> object: + """Confirm list of key-value pairs. The third component in the tuple should be True if + the value is to be rendered as binary with monospace font, False otherwise. + This only concerns the text style, you need to decode the value to UTF-8 in python.""" + + # rust/src/ui/model_tr/layout.rs def confirm_output_r( *, diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 8ee4128a8e..a20f39e0a3 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING, Sequence +from ubinascii import hexlify from trezor import io, log, loop, ui, wire, workflow from trezor.enums import ButtonRequestType @@ -910,13 +911,25 @@ async def confirm_properties( hold: bool = False, br_code: ButtonRequestType = ButtonRequestType.ConfirmOutput, ) -> None: - await _placeholder_confirm( - ctx=ctx, - br_type=br_type, - title=title.upper(), - data="\n\n".join(f"{name or ''}\n{value or ''}" for name, value in props), - description="", - br_code=br_code, + def handle_bytes(prop: PropertyType): + if isinstance(prop[1], bytes): + return (prop[0], hexlify(prop[1]).decode(), True) + else: + return (prop[0], prop[1], False) + + await raise_if_cancelled( + interact( + ctx, + RustLayout( + trezorui2.confirm_properties( + title=title.upper(), + items=map(handle_bytes, props), + hold=hold, + ) + ), + br_type, + br_code, + ) )