diff --git a/core/embed/rust/src/ui/api/firmware_upy.rs b/core/embed/rust/src/ui/api/firmware_upy.rs index 3f7cfb153a..cb9a0b78bc 100644 --- a/core/embed/rust/src/ui/api/firmware_upy.rs +++ b/core/embed/rust/src/ui/api/firmware_upy.rs @@ -184,6 +184,23 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = move |_args: &[Obj], kwargs: &Map| { + let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; + let info_button: TString = kwargs.get(Qstr::MP_QSTR_info_button)?.try_into()?; + let verb_cancel: Option> = kwargs + .get(Qstr::MP_QSTR_verb_cancel) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; + + let layout = ModelUI::confirm_with_info(title, button, info_button, verb_cancel, items)?; + Ok(LayoutObj::new_root(layout)?.into()) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_continue_recovery_homepage( n_args: usize, args: *const Obj, @@ -794,6 +811,19 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// """Confirm TOS before creating wallet creation or wallet recovery.""" Qstr::MP_QSTR_confirm_reset_device => obj_fn_kw!(0, new_confirm_reset_device).as_obj(), + /// def confirm_with_info( + /// *, + /// title: str, + /// button: str, + /// info_button: str, + /// verb_cancel: str | None = None, + /// items: Iterable[tuple[int, str | bytes]], + /// ) -> LayoutObj[UiResult]: + /// """Confirm given items but with third button. Always single page + /// without scrolling. In mercury, the button is placed in + /// context menu.""" + Qstr::MP_QSTR_confirm_with_info => obj_fn_kw!(0, new_confirm_with_info).as_obj(), + /// def continue_recovery_homepage( /// *, /// text: str, diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 4f4ba0251d..7d04b11e14 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -658,32 +658,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; - let info_button: TString = kwargs.get(Qstr::MP_QSTR_info_button)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - - let mut paragraphs = ParagraphVecShort::new(); - - for para in IterBuf::new().try_iterate(items)? { - let [font, text]: [Obj; 2] = util::iter_into_array(para)?; - let style: &TextStyle = theme::textstyle_number(font.try_into()?); - let text: TString = text.try_into()?; - paragraphs.add(Paragraph::new(style, text)); - if paragraphs.is_full() { - break; - } - } - - let flow = - confirm_with_info::new_confirm_with_info(title, button, info_button, paragraphs)?; - Ok(LayoutObj::new_root(flow)?.into()) - }; - 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: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; @@ -875,17 +849,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Transaction summary. Always hold to confirm.""" Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(), - /// def confirm_with_info( - /// *, - /// title: str, - /// button: str, - /// info_button: str, - /// items: Iterable[tuple[int, str]], - /// ) -> LayoutObj[UiResult]: - /// """Confirm given items but with third button. In mercury, the button is placed in - /// context menu.""" - Qstr::MP_QSTR_confirm_with_info => obj_fn_kw!(0, new_confirm_with_info).as_obj(), - /// def confirm_more( /// *, /// title: str, diff --git a/core/embed/rust/src/ui/model_mercury/ui_features_fw.rs b/core/embed/rust/src/ui/model_mercury/ui_features_fw.rs index c15f5c5c75..1f99091f83 100644 --- a/core/embed/rust/src/ui/model_mercury/ui_features_fw.rs +++ b/core/embed/rust/src/ui/model_mercury/ui_features_fw.rs @@ -3,16 +3,19 @@ use core::cmp::Ordering; use crate::{ error::{value_error, Error}, io::BinaryData, - micropython::{gc::Gc, list::List, util}, + micropython::{gc::Gc, iter::IterBuf, list::List, obj::Obj, util}, strutil::TString, translations::TR, ui::{ component::{ connect::Connect, swipe_detect::SwipeSettings, - text::paragraphs::{ - Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, - Paragraphs, VecExt, + text::{ + paragraphs::{ + Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, + Paragraphs, VecExt, + }, + TextStyle, }, Border, CachedJpeg, ComponentExt, Empty, Never, Timeout, }, @@ -31,7 +34,9 @@ use super::{ MnemonicKeyboard, PinKeyboard, Progress, SelectWordCount, Slip39Input, StatusScreen, SwipeContent, SwipeUpScreen, VerticalMenu, }, - flow::{self, new_confirm_action_simple, ConfirmActionMenu, ConfirmActionStrings}, + flow::{ + self, confirm_with_info, new_confirm_action_simple, ConfirmActionMenu, ConfirmActionStrings, + }, theme, ModelMercuryFeatures, }; @@ -225,6 +230,30 @@ impl UIFeaturesFirmware for ModelMercuryFeatures { Ok(flow) } + fn confirm_with_info( + title: TString<'static>, + button: TString<'static>, + info_button: TString<'static>, + verb_cancel: Option>, + items: Obj, + ) -> Result { + let mut paragraphs = ParagraphVecShort::new(); + + for para in IterBuf::new().try_iterate(items)? { + let [font, text]: [Obj; 2] = util::iter_into_array(para)?; + let style: &TextStyle = theme::textstyle_number(font.try_into()?); + let text: TString = text.try_into()?; + paragraphs.add(Paragraph::new(style, text)); + if paragraphs.is_full() { + break; + } + } + + let flow = + confirm_with_info::new_confirm_with_info(title, button, info_button, paragraphs)?; + Ok(flow) + } + fn check_homescreen_format(image: BinaryData, __accept_toif: bool) -> bool { super::component::check_homescreen_format(image) } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index 16b66c11fa..3ef8ac4913 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -778,41 +778,6 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs: unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; - let verb_cancel: Option> = kwargs - .get(Qstr::MP_QSTR_verb_cancel) - .unwrap_or_else(|_| Obj::const_none()) - .try_into_option()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - - let mut paragraphs = ParagraphVecShort::new(); - - for para in IterBuf::new().try_iterate(items)? { - let [font, text]: [Obj; 2] = util::iter_into_array(para)?; - let style: &TextStyle = theme::textstyle_number(font.try_into()?); - let text: TString = text.try_into()?; - paragraphs.add(Paragraph::new(style, text)); - if paragraphs.is_full() { - break; - } - } - - let obj = LayoutObj::new(Frame::new( - title, - ShowMore::>::new( - paragraphs.into_paragraphs(), - verb_cancel, - button, - ), - ))?; - Ok(obj.into()) - }; - 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: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; @@ -976,18 +941,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Show multiple texts, each on its own page.""" Qstr::MP_QSTR_multiple_pages_texts => obj_fn_kw!(0, new_multiple_pages_texts).as_obj(), - /// def confirm_with_info( - /// *, - /// title: str, - /// button: str, - /// info_button: str, # unused on TR - /// items: Iterable[Tuple[int, str | bytes]], - /// verb_cancel: str | None = None, - /// ) -> LayoutObj[UiResult]: - /// """Confirm given items but with third button. Always single page - /// without scrolling.""" - Qstr::MP_QSTR_confirm_with_info => obj_fn_kw!(0, new_confirm_with_info).as_obj(), - /// def confirm_more( /// *, /// title: str, diff --git a/core/embed/rust/src/ui/model_tr/ui_features_fw.rs b/core/embed/rust/src/ui/model_tr/ui_features_fw.rs index 284eff5a53..de0ca17443 100644 --- a/core/embed/rust/src/ui/model_tr/ui_features_fw.rs +++ b/core/embed/rust/src/ui/model_tr/ui_features_fw.rs @@ -4,7 +4,7 @@ use crate::{ error::Error, io::BinaryData, maybe_trace::MaybeTrace, - micropython::{gc::Gc, list::List}, + micropython::{gc::Gc, iter::IterBuf, list::List, obj::Obj, util}, strutil::TString, translations::TR, ui::{ @@ -16,6 +16,7 @@ use crate::{ Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, Paragraphs, VecExt, }, + TextStyle, }, Component, ComponentExt, Empty, FormattedText, Label, LineBreaking, Paginate, Timeout, }, @@ -36,7 +37,7 @@ use super::{ component::{ ButtonDetails, ButtonPage, CoinJoinProgress, ConfirmHomescreen, Flow, FlowPages, Frame, Homescreen, Lockscreen, NumberInput, PassphraseEntry, PinEntry, Progress, ScrollableFrame, - ShareWords, SimpleChoice, WordlistEntry, WordlistType, + ShareWords, ShowMore, SimpleChoice, WordlistEntry, WordlistType, }, theme, ModelTRFeatures, }; @@ -290,6 +291,36 @@ impl UIFeaturesFirmware for ModelTRFeatures { content_in_button_page(title, formatted, button, Some("".into()), false) } + fn confirm_with_info( + title: TString<'static>, + button: TString<'static>, + _info_button: TString<'static>, + verb_cancel: Option>, + items: Obj, + ) -> Result { + let mut paragraphs = ParagraphVecShort::new(); + + for para in IterBuf::new().try_iterate(items)? { + let [font, text]: [Obj; 2] = util::iter_into_array(para)?; + let style: &TextStyle = theme::textstyle_number(font.try_into()?); + let text: TString = text.try_into()?; + paragraphs.add(Paragraph::new(style, text)); + if paragraphs.is_full() { + break; + } + } + + let layout = RootComponent::new(Frame::new( + title, + ShowMore::>::new( + paragraphs.into_paragraphs(), + verb_cancel, + button, + ), + )); + Ok(layout) + } + fn check_homescreen_format(image: BinaryData, _accept_toif: bool) -> bool { super::component::check_homescreen_format(image) } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 206cb2198b..43f3bf89cc 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -702,37 +702,6 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { - let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; - let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; - let info_button: TString = kwargs.get(Qstr::MP_QSTR_info_button)?.try_into()?; - let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - - let mut paragraphs = ParagraphVecShort::new(); - - for para in IterBuf::new().try_iterate(items)? { - let [font, text]: [Obj; 2] = util::iter_into_array(para)?; - let style: &TextStyle = theme::textstyle_number(font.try_into()?); - let text: TString = text.try_into()?; - paragraphs.add(Paragraph::new(style, text)); - if paragraphs.is_full() { - break; - } - } - - let buttons = Button::cancel_info_confirm(button, info_button); - - let obj = LayoutObj::new(Frame::left_aligned( - theme::label_title(), - title, - Dialog::new(paragraphs.into_paragraphs(), buttons), - ))?; - Ok(obj.into()) - }; - 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: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; @@ -875,17 +844,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Transaction summary. Always hold to confirm.""" Qstr::MP_QSTR_confirm_total => obj_fn_kw!(0, new_confirm_total).as_obj(), - /// def confirm_with_info( - /// *, - /// title: str, - /// button: str, - /// info_button: str, - /// items: Iterable[tuple[int, str | bytes]], - /// ) -> LayoutObj[UiResult]: - /// """Confirm given items but with third button. Always single page - /// without scrolling.""" - Qstr::MP_QSTR_confirm_with_info => obj_fn_kw!(0, new_confirm_with_info).as_obj(), - /// def confirm_more( /// *, /// title: str, diff --git a/core/embed/rust/src/ui/model_tt/ui_features_fw.rs b/core/embed/rust/src/ui/model_tt/ui_features_fw.rs index a707048b3d..defa475a79 100644 --- a/core/embed/rust/src/ui/model_tt/ui_features_fw.rs +++ b/core/embed/rust/src/ui/model_tt/ui_features_fw.rs @@ -3,16 +3,19 @@ use core::cmp::Ordering; use crate::{ error::{value_error, Error}, io::BinaryData, - micropython::{gc::Gc, list::List}, + micropython::{gc::Gc, iter::IterBuf, list::List, obj::Obj, util}, strutil::TString, translations::TR, ui::{ component::{ connect::Connect, image::BlendedImage, - text::paragraphs::{ - Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, - Paragraphs, VecExt, + text::{ + paragraphs::{ + Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort, + Paragraphs, VecExt, + }, + TextStyle, }, Border, ComponentExt, Empty, Jpeg, Label, Never, Timeout, }, @@ -272,6 +275,35 @@ impl UIFeaturesFirmware for ModelTTFeatures { Ok(layout) } + fn confirm_with_info( + title: TString<'static>, + button: TString<'static>, + info_button: TString<'static>, + _verb_cancel: Option>, + items: Obj, + ) -> Result { + let mut paragraphs = ParagraphVecShort::new(); + + for para in IterBuf::new().try_iterate(items)? { + let [font, text]: [Obj; 2] = util::iter_into_array(para)?; + let style: &TextStyle = theme::textstyle_number(font.try_into()?); + let text: TString = text.try_into()?; + paragraphs.add(Paragraph::new(style, text)); + if paragraphs.is_full() { + break; + } + } + + let buttons = Button::cancel_info_confirm(button, info_button); + + let layout = RootComponent::new(Frame::left_aligned( + theme::label_title(), + title, + Dialog::new(paragraphs.into_paragraphs(), buttons), + )); + Ok(layout) + } + fn check_homescreen_format(image: BinaryData, accept_toif: bool) -> bool { super::component::check_homescreen_format(image, false) } diff --git a/core/embed/rust/src/ui/ui_features_fw.rs b/core/embed/rust/src/ui/ui_features_fw.rs index 0eb9e4eeab..cf554f6f7c 100644 --- a/core/embed/rust/src/ui/ui_features_fw.rs +++ b/core/embed/rust/src/ui/ui_features_fw.rs @@ -64,6 +64,14 @@ pub trait UIFeaturesFirmware { fn confirm_reset_device(recovery: bool) -> Result; + fn confirm_with_info( + title: TString<'static>, + button: TString<'static>, + info_button: TString<'static>, + verb_cancel: Option>, + items: Obj, // TODO: replace Obj + ) -> Result; + fn continue_recovery_homepage( text: TString<'static>, subtext: Option>, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 7c221b8f9d..f102160a95 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -108,18 +108,6 @@ def confirm_total( """Transaction summary. Always hold to confirm.""" -# rust/src/ui/model_mercury/layout.rs -def confirm_with_info( - *, - title: str, - button: str, - info_button: str, - items: Iterable[tuple[int, str]], -) -> LayoutObj[UiResult]: - """Confirm given items but with third button. In mercury, the button is placed in - context menu.""" - - # rust/src/ui/model_mercury/layout.rs def confirm_more( *, @@ -350,19 +338,6 @@ def multiple_pages_texts( """Show multiple texts, each on its own page.""" -# rust/src/ui/model_tr/layout.rs -def confirm_with_info( - *, - title: str, - button: str, - info_button: str, # unused on TR - items: Iterable[Tuple[int, str | bytes]], - verb_cancel: str | None = None, -) -> LayoutObj[UiResult]: - """Confirm given items but with third button. Always single page - without scrolling.""" - - # rust/src/ui/model_tr/layout.rs def confirm_more( *, @@ -486,18 +461,6 @@ def confirm_total( """Transaction summary. Always hold to confirm.""" -# rust/src/ui/model_tt/layout.rs -def confirm_with_info( - *, - title: str, - button: str, - info_button: str, - items: Iterable[tuple[int, str | bytes]], -) -> LayoutObj[UiResult]: - """Confirm given items but with third button. Always single page - without scrolling.""" - - # rust/src/ui/model_tt/layout.rs def confirm_more( *, diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index 9080a21632..2d80b98348 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -163,6 +163,20 @@ def confirm_reset_device(recovery: bool) -> LayoutObj[UiResult]: """Confirm TOS before creating wallet creation or wallet recovery.""" +# rust/src/ui/api/firmware_upy.rs +def confirm_with_info( + *, + title: str, + button: str, + info_button: str, + verb_cancel: str | None = None, + items: Iterable[tuple[int, str | bytes]], +) -> LayoutObj[UiResult]: + """Confirm given items but with third button. Always single page + without scrolling. In mercury, the button is placed in + context menu.""" + + # rust/src/ui/api/firmware_upy.rs def continue_recovery_homepage( *, diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index bf1c589a22..5937cef6c3 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -394,7 +394,7 @@ async def should_show_payment_request_details( Raises ActionCancelled if the user cancels. """ result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=TR.send__title_sending, items=[(ui.NORMAL, f"{amount} to\n{recipient_name}")] + [(ui.NORMAL, memo) for memo in memos], @@ -431,7 +431,7 @@ async def should_show_more( confirm = TR.buttons__confirm result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=title, items=para, button=confirm, diff --git a/core/src/trezor/ui/layouts/tr/__init__.py b/core/src/trezor/ui/layouts/tr/__init__.py index 7dab84b7ad..0159b290c3 100644 --- a/core/src/trezor/ui/layouts/tr/__init__.py +++ b/core/src/trezor/ui/layouts/tr/__init__.py @@ -525,11 +525,11 @@ async def should_show_more( confirm = TR.buttons__confirm result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=title, items=para, button=confirm, - verb_cancel=verb_cancel, # type: ignore [No parameter named "verb_cancel"] + verb_cancel=verb_cancel, info_button=button_text, # unused on TR ), br_name, @@ -732,7 +732,7 @@ async def confirm_value( send_button_request = True while True: result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=title, items=((ui.NORMAL, value),), button=verb or TR.buttons__confirm, diff --git a/core/src/trezor/ui/layouts/tt/__init__.py b/core/src/trezor/ui/layouts/tt/__init__.py index 89fdbd3939..aebf7cf00c 100644 --- a/core/src/trezor/ui/layouts/tt/__init__.py +++ b/core/src/trezor/ui/layouts/tt/__init__.py @@ -451,7 +451,7 @@ async def should_show_payment_request_details( Raises ActionCancelled if the user cancels. """ result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=TR.send__title_sending, items=[(ui.NORMAL, f"{amount} to\n{recipient_name}")] + [(ui.NORMAL, memo) for memo in memos], @@ -488,7 +488,7 @@ async def should_show_more( confirm = TR.buttons__confirm result = await interact( - trezorui2.confirm_with_info( + trezorui_api.confirm_with_info( title=title, items=para, button=confirm,