1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 07:28:10 +00:00

refactor(core): move request_number to UiFeatures

- in addition, to unify the trait function arguments, ButtonRequest was
moved from Rust side to uPy side for mercury
This commit is contained in:
obrusvit 2024-11-01 15:21:19 +01:00
parent 83e0b23ad1
commit 91a9f5f3a0
15 changed files with 156 additions and 175 deletions

View File

@ -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;

View File

@ -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<TString> = kwargs
.get(Qstr::MP_QSTR_description)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let more_info_callback: Option<Obj> = 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,

View File

@ -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<SwipeFlow, error::Error> {
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(),

View File

@ -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,

View File

@ -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<TString<'static>>,
more_info_callback: Option<impl Fn(u32) -> TString<'static> + 'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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>,

View File

@ -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

View File

@ -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<TString<'static>>,
_more_info_callback: Option<impl Fn(u32) -> TString<'static> + 'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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>,

View File

@ -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,

View File

@ -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<TString<'static>>,
more_info_callback: Option<impl Fn(u32) -> TString<'static> + 'static>,
) -> Result<impl LayoutMaybeTrace, Error> {
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>,

View File

@ -44,6 +44,15 @@ pub trait UIFeaturesFirmware {
can_go_back: bool,
) -> Result<impl LayoutMaybeTrace, Error>;
fn request_number(
title: TString<'static>,
count: u32,
min_count: u32,
max_count: u32,
description: Option<TString<'static>>,
more_info_callback: Option<impl Fn(u32) -> TString<'static> + 'static>,
) -> Result<impl LayoutMaybeTrace, Error>;
fn request_pin(
prompt: TString<'static>,
subprompt: TString<'static>,

View File

@ -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(
*,

View File

@ -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(
*,

View File

@ -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:

View File

@ -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,

View File

@ -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: