diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index f400326e9b..f72e2a42e2 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -257,7 +257,6 @@ static void _librust_qstrs(void) { MP_QSTR_flow_continue_recovery; MP_QSTR_flow_get_address; MP_QSTR_flow_prompt_backup; - MP_QSTR_flow_request_number; MP_QSTR_flow_show_share_words; MP_QSTR_flow_warning_hi_prio; MP_QSTR_get_language; @@ -285,7 +284,6 @@ static void _librust_qstrs(void) { MP_QSTR_icon_name; MP_QSTR_image; MP_QSTR_indeterminate; - MP_QSTR_info; MP_QSTR_info_button; MP_QSTR_init; MP_QSTR_inputs__back; @@ -352,6 +350,7 @@ static void _librust_qstrs(void) { MP_QSTR_modify_fee__no_change; MP_QSTR_modify_fee__title; MP_QSTR_modify_fee__transaction_fee; + MP_QSTR_more_info_callback; MP_QSTR_multiple_pages_texts; MP_QSTR_notification; MP_QSTR_notification_level; diff --git a/core/embed/rust/src/ui/api/firmware_upy.rs b/core/embed/rust/src/ui/api/firmware_upy.rs index b4ce2b9ae0..5fcceb3995 100644 --- a/core/embed/rust/src/ui/api/firmware_upy.rs +++ b/core/embed/rust/src/ui/api/firmware_upy.rs @@ -134,6 +134,42 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_request_number(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 count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; + let min_count: u32 = kwargs.get(Qstr::MP_QSTR_min_count)?.try_into()?; + let max_count: u32 = kwargs.get(Qstr::MP_QSTR_max_count)?.try_into()?; + let description: Option = kwargs + .get(Qstr::MP_QSTR_description) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + let more_info_callback: Option = kwargs + .get(Qstr::MP_QSTR_more_info_callback) + .unwrap_or_else(|_| Obj::const_none()) + .try_into_option()?; + + let more_info_cb = more_info_callback.and_then(|callback| { + let cb = move |n: u32| { + let text = callback.call_with_n_args(&[n.try_into().unwrap()]).unwrap(); + TString::try_from(text).unwrap() + }; + Some(cb) + }); + + let layout = ModelUI::request_number( + title, + count, + min_count, + max_count, + description, + more_info_cb, + )?; + Ok(LayoutObj::new_root(layout)?.into()) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; @@ -451,6 +487,19 @@ pub static mp_module_trezorui_api: Module = obj_module! { /// """SLIP39 word input keyboard.""" Qstr::MP_QSTR_request_slip39 => obj_fn_kw!(0, new_request_slip39).as_obj(), + /// def request_number( + /// *, + /// title: str, + /// count: int, + /// min_count: int, + /// max_count: int, + /// description: str | None = None, + /// more_info_callback: Callable[[int], str] | None = None, + /// ) -> LayoutObj[tuple[UiResult, int]]: + /// """Number input with + and - buttons, optional static description and optional dynamic + /// description.""" + Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(), + /// def request_pin( /// *, /// prompt: str, diff --git a/core/embed/rust/src/ui/model_mercury/flow/request_number.rs b/core/embed/rust/src/ui/model_mercury/flow/request_number.rs index 9bfce9414d..2f74939111 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/request_number.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/request_number.rs @@ -67,8 +67,6 @@ pub fn new_request_number( max_count: u32, description: TString<'static>, info_closure: impl Fn(u32) -> TString<'static> + 'static, - br_code: u16, - br_name: TString<'static>, ) -> Result { NUM_DISPLAYED.store(count as u16, Ordering::Relaxed); @@ -94,8 +92,7 @@ pub fn new_request_number( NUM_DISPLAYED.store(n as u16, Ordering::Relaxed); Some(FlowMsg::Choice(n as usize)) } - }) - .one_button_request(ButtonRequest::from_num(br_code, br_name)); + }); let content_menu = Frame::left_aligned( TString::empty(), diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index a8660167e3..5d01b0996e 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -1050,41 +1050,6 @@ extern "C" fn new_prompt_backup() -> Obj { unsafe { util::try_or_raise(block) } } -extern "C" fn new_request_number(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 count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; - let min_count: u32 = kwargs.get(Qstr::MP_QSTR_min_count)?.try_into()?; - let max_count: u32 = kwargs.get(Qstr::MP_QSTR_max_count)?.try_into()?; - let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; - let info_cb: Obj = kwargs.get(Qstr::MP_QSTR_info)?; - assert!(info_cb != Obj::const_none()); - let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?; - let br_name: TString = kwargs.get(Qstr::MP_QSTR_br_name)?.try_into()?; - - let mp_info_closure = move |num: u32| { - // TODO: Handle error - let text = info_cb - .call_with_n_args(&[num.try_into().unwrap()]) - .unwrap(); - TString::try_from(text).unwrap() - }; - - let flow = flow::request_number::new_request_number( - title, - count, - min_count, - max_count, - description, - mp_info_closure, - br_code, - br_name, - )?; - Ok(LayoutObj::new_root(flow)?.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - extern "C" fn new_show_checklist(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()?; @@ -1424,21 +1389,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// confirmation.""" Qstr::MP_QSTR_flow_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(), - /// def flow_request_number( - /// *, - /// title: str, - /// count: int, - /// min_count: int, - /// max_count: int, - /// description: str, - /// info: Callable[[int], str] | None = None, - /// br_code: ButtonRequestType, - /// br_name: str, - /// ) -> LayoutObj[tuple[UiResult, int]]: - /// """Number input with + and - buttons, description, and context menu with cancel and - /// info.""" - Qstr::MP_QSTR_flow_request_number => obj_fn_kw!(0, new_request_number).as_obj(), - /// def show_checklist( /// *, /// 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 30e3714777..0d54d1b1b7 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 @@ -6,7 +6,10 @@ use crate::{ translations::TR, ui::{ component::{ - connect::Connect, swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs}, CachedJpeg, ComponentExt, Never, Timeout + connect::Connect, + swipe_detect::SwipeSettings, + text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs}, + CachedJpeg, ComponentExt, Never, Timeout, }, geometry::Direction, layout::{ @@ -130,6 +133,33 @@ impl UIFeaturesFirmware for ModelMercuryFeatures { Ok(layout) } + fn request_number( + title: TString<'static>, + count: u32, + min_count: u32, + max_count: u32, + description: Option>, + more_info_callback: Option TString<'static> + 'static>, + ) -> Result { + debug_assert!( + description.is_some(), + "Description is required for request_number" + ); + debug_assert!( + more_info_callback.is_some(), + "More info callback is required for request_number" + ); + let flow = flow::request_number::new_request_number( + title, + count, + min_count, + max_count, + description.unwrap(), + more_info_callback.unwrap(), + )?; + Ok(flow) + } + fn request_pin( prompt: TString<'static>, subprompt: TString<'static>, diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index e05efc84b8..c8de9d6aac 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -1205,21 +1205,6 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_request_number(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 min_count: u32 = kwargs.get(Qstr::MP_QSTR_min_count)?.try_into()?; - let max_count: u32 = kwargs.get(Qstr::MP_QSTR_max_count)?.try_into()?; - let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; - - let obj = LayoutObj::new( - Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(), - )?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let button: TString<'static> = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; @@ -1563,17 +1548,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Shows a backup seed.""" Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(), - /// def request_number( - /// *, - /// title: str, - /// count: int, - /// min_count: int, - /// max_count: int, - /// description: Callable[[int], str] | None = None, # unused on TR - /// ) -> LayoutObj[tuple[UiResult, int]]: - /// """Number input with + and - buttons, description, and info button.""" - Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(), - /// def show_checklist( /// *, /// title: str, # unused on TR 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 39eca14f99..3c86922833 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 @@ -22,8 +22,8 @@ use crate::{ use super::{ component::{ ButtonDetails, ButtonPage, CoinJoinProgress, ConfirmHomescreen, Frame, Homescreen, - Lockscreen, PassphraseEntry, PinEntry, Progress, ScrollableFrame, SimpleChoice, - WordlistEntry, WordlistType, + Lockscreen, NumberInput, PassphraseEntry, PinEntry, Progress, ScrollableFrame, + SimpleChoice, WordlistEntry, WordlistType, }, theme, ModelTRFeatures, }; @@ -143,6 +143,20 @@ impl UIFeaturesFirmware for ModelTRFeatures { Ok(layout) } + fn request_number( + title: TString<'static>, + count: u32, + min_count: u32, + max_count: u32, + _description: Option>, + _more_info_callback: Option TString<'static> + 'static>, + ) -> Result { + let layout = RootComponent::new( + Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(), + ); + Ok(layout) + } + fn request_pin( prompt: TString<'static>, subprompt: TString<'static>, diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 076e0069b6..8c32f975ed 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1144,34 +1144,6 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } -extern "C" fn new_request_number(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 min_count: u32 = kwargs.get(Qstr::MP_QSTR_min_count)?.try_into()?; - let max_count: u32 = kwargs.get(Qstr::MP_QSTR_max_count)?.try_into()?; - let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; - let description_callback: Obj = kwargs.get(Qstr::MP_QSTR_description)?; - assert!(description_callback != Obj::const_none()); - - let callback = move |i: u32| { - TString::try_from( - description_callback - .call_with_n_args(&[i.try_into().unwrap()]) - .unwrap(), - ) - .unwrap() - }; - - let obj = LayoutObj::new(Frame::left_aligned( - theme::label_title(), - title, - NumberInputDialog::new(min_count, max_count, count, callback)?, - ))?; - Ok(obj.into()) - }; - unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } -} - extern "C" fn new_show_checklist(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()?; @@ -1539,17 +1511,6 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Show mnemonic for backup. Expects the words pre-divided into individual pages.""" Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(), - /// def request_number( - /// *, - /// title: str, - /// count: int, - /// min_count: int, - /// max_count: int, - /// description: Callable[[int], str] | None = None, - /// ) -> LayoutObj[tuple[UiResult, int]]: - /// """Number input with + and - buttons, description, and info button.""" - Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(), - /// def show_checklist( /// *, /// 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 4c27b17b9f..e5083249e6 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 @@ -6,7 +6,10 @@ use crate::{ translations::TR, ui::{ component::{ - connect::Connect, image::BlendedImage, text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt}, ComponentExt, Empty, Jpeg, Label, Never, Timeout + connect::Connect, + image::BlendedImage, + text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt}, + ComponentExt, Empty, Jpeg, Label, Never, Timeout, }, layout::{ obj::{LayoutMaybeTrace, LayoutObj, RootComponent}, @@ -18,7 +21,10 @@ use crate::{ use super::{ component::{ - check_homescreen_format, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet, CancelConfirmMsg, CoinJoinProgress, Dialog, Frame, Homescreen, IconDialog, Lockscreen, MnemonicKeyboard, PassphraseKeyboard, PinKeyboard, Progress, SelectWordCount, SetBrightnessDialog, Slip39Input + check_homescreen_format, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet, + CancelConfirmMsg, CoinJoinProgress, Dialog, Frame, Homescreen, IconDialog, Lockscreen, + MnemonicKeyboard, NumberInputDialog, PassphraseKeyboard, PinKeyboard, Progress, + SelectWordCount, SetBrightnessDialog, Slip39Input, }, theme, ModelTTFeatures, }; @@ -142,6 +148,23 @@ impl UIFeaturesFirmware for ModelTTFeatures { Ok(layout) } + fn request_number( + title: TString<'static>, + count: u32, + min_count: u32, + max_count: u32, + _description: Option>, + more_info_callback: Option TString<'static> + 'static>, + ) -> Result { + debug_assert!(more_info_callback.is_some()); + let layout = RootComponent::new(Frame::left_aligned( + theme::label_title(), + title, + NumberInputDialog::new(min_count, max_count, count, more_info_callback.unwrap())?, + )); + Ok(layout) + } + fn request_pin( prompt: TString<'static>, subprompt: TString<'static>, diff --git a/core/embed/rust/src/ui/ui_features_fw.rs b/core/embed/rust/src/ui/ui_features_fw.rs index 9cc8f4ac1d..46bf6d4428 100644 --- a/core/embed/rust/src/ui/ui_features_fw.rs +++ b/core/embed/rust/src/ui/ui_features_fw.rs @@ -44,6 +44,15 @@ pub trait UIFeaturesFirmware { can_go_back: bool, ) -> Result; + fn request_number( + title: TString<'static>, + count: u32, + min_count: u32, + max_count: u32, + description: Option>, + more_info_callback: Option TString<'static> + 'static>, + ) -> Result; + fn request_pin( prompt: TString<'static>, subprompt: TString<'static>, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 0fa4578188..44ed2295fa 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -252,22 +252,6 @@ def flow_show_share_words( confirmation.""" -# rust/src/ui/model_mercury/layout.rs -def flow_request_number( - *, - title: str, - count: int, - min_count: int, - max_count: int, - description: str, - info: Callable[[int], str] | None = None, - br_code: ButtonRequestType, - br_name: str, -) -> LayoutObj[tuple[UiResult, int]]: - """Number input with + and - buttons, description, and context menu with cancel and - info.""" - - # rust/src/ui/model_mercury/layout.rs def show_checklist( *, @@ -629,18 +613,6 @@ def show_share_words( """Shows a backup seed.""" -# rust/src/ui/model_tr/layout.rs -def request_number( - *, - title: str, - count: int, - min_count: int, - max_count: int, - description: Callable[[int], str] | None = None, # unused on TR -) -> LayoutObj[tuple[UiResult, int]]: - """Number input with + and - buttons, description, and info button.""" - - # rust/src/ui/model_tr/layout.rs def show_checklist( *, @@ -954,18 +926,6 @@ def show_share_words( """Show mnemonic for backup. Expects the words pre-divided into individual pages.""" -# rust/src/ui/model_tt/layout.rs -def request_number( - *, - title: str, - count: int, - min_count: int, - max_count: int, - description: Callable[[int], str] | None = None, -) -> LayoutObj[tuple[UiResult, int]]: - """Number input with + and - buttons, description, and info button.""" - - # rust/src/ui/model_tt/layout.rs def show_checklist( *, diff --git a/core/mocks/generated/trezorui_api.pyi b/core/mocks/generated/trezorui_api.pyi index 8c3308c7e7..01b722898e 100644 --- a/core/mocks/generated/trezorui_api.pyi +++ b/core/mocks/generated/trezorui_api.pyi @@ -134,6 +134,20 @@ def request_slip39( """SLIP39 word input keyboard.""" +# rust/src/ui/api/firmware_upy.rs +def request_number( + *, + title: str, + count: int, + min_count: int, + max_count: int, + description: str | None = None, + more_info_callback: Callable[[int], str] | None = None, +) -> LayoutObj[tuple[UiResult, int]]: + """Number input with + and - buttons, optional static description and optional dynamic + description.""" + + # rust/src/ui/api/firmware_upy.rs def request_pin( *, diff --git a/core/src/trezor/ui/layouts/mercury/reset.py b/core/src/trezor/ui/layouts/mercury/reset.py index de8ca557ea..d5ca027b66 100644 --- a/core/src/trezor/ui/layouts/mercury/reset.py +++ b/core/src/trezor/ui/layouts/mercury/reset.py @@ -155,17 +155,17 @@ async def _prompt_number( br_name: str, ) -> int: result = await interact( - trezorui2.flow_request_number( + trezorui_api.request_number( title=title, - description=description, count=count, min_count=min_count, max_count=max_count, - info=info, - br_code=ButtonRequestType.ResetDevice, - br_name=br_name, + description=description, + more_info_callback=info, ), - None, + br_name, + ButtonRequestType.ResetDevice, + raise_on_cancel=None, ) if __debug__ and result is CONFIRMED: diff --git a/core/src/trezor/ui/layouts/tr/reset.py b/core/src/trezor/ui/layouts/tr/reset.py index 6b82a7efcc..586d63c7bb 100644 --- a/core/src/trezor/ui/layouts/tr/reset.py +++ b/core/src/trezor/ui/layouts/tr/reset.py @@ -140,7 +140,7 @@ async def _prompt_number( max_count: int, br_name: str, ) -> int: - num_input = trezorui2.request_number( + num_input = trezorui_api.request_number( title=title, count=count, min_count=min_count, diff --git a/core/src/trezor/ui/layouts/tt/reset.py b/core/src/trezor/ui/layouts/tt/reset.py index aedef51eac..82b1a40c4c 100644 --- a/core/src/trezor/ui/layouts/tt/reset.py +++ b/core/src/trezor/ui/layouts/tt/reset.py @@ -140,12 +140,13 @@ async def _prompt_number( max_count: int, br_name: str, ) -> int: - num_input = trezorui2.request_number( + num_input = trezorui_api.request_number( title=title, - description=description, count=count, min_count=min_count, max_count=max_count, + description=None, + more_info_callback=description, ) while True: