mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-30 01:58:11 +00:00
feat(core/ui): T3T1 request number flow
[no changelog]
This commit is contained in:
parent
9c14cae656
commit
43eeccac59
core
embed/rust
mocks/generated
src/trezor/ui/layouts/mercury
@ -230,6 +230,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_flow_confirm_summary;
|
MP_QSTR_flow_confirm_summary;
|
||||||
MP_QSTR_flow_get_address;
|
MP_QSTR_flow_get_address;
|
||||||
MP_QSTR_flow_prompt_backup;
|
MP_QSTR_flow_prompt_backup;
|
||||||
|
MP_QSTR_flow_request_number;
|
||||||
MP_QSTR_flow_show_share_words;
|
MP_QSTR_flow_show_share_words;
|
||||||
MP_QSTR_flow_warning_hi_prio;
|
MP_QSTR_flow_warning_hi_prio;
|
||||||
MP_QSTR_get_language;
|
MP_QSTR_get_language;
|
||||||
@ -253,6 +254,7 @@ static void _librust_qstrs(void) {
|
|||||||
MP_QSTR_icon_name;
|
MP_QSTR_icon_name;
|
||||||
MP_QSTR_image;
|
MP_QSTR_image;
|
||||||
MP_QSTR_indeterminate;
|
MP_QSTR_indeterminate;
|
||||||
|
MP_QSTR_info;
|
||||||
MP_QSTR_info_button;
|
MP_QSTR_info_button;
|
||||||
MP_QSTR_init;
|
MP_QSTR_init;
|
||||||
MP_QSTR_inputs__back;
|
MP_QSTR_inputs__back;
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
strutil::{self, TString},
|
strutil::{self, TString},
|
||||||
translations::TR,
|
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::ComponentExt,
|
base::ComponentExt,
|
||||||
paginated::Paginate,
|
paginated::Paginate,
|
||||||
text::paragraphs::{Paragraph, Paragraphs},
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
Child, Component, Event, EventCtx, Pad,
|
Child, Component, Event, EventCtx, Pad, SwipeDirection,
|
||||||
},
|
},
|
||||||
display::{self, Font},
|
display::Font,
|
||||||
|
flow::{Swipable, SwipableResult},
|
||||||
geometry::{Alignment, Grid, Insets, Offset, Rect},
|
geometry::{Alignment, Grid, Insets, Offset, Rect},
|
||||||
shape::{self, Renderer},
|
shape::{self, Renderer},
|
||||||
},
|
},
|
||||||
@ -17,10 +17,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::{theme, Button, ButtonMsg};
|
use super::{theme, Button, ButtonMsg};
|
||||||
|
|
||||||
pub enum NumberInputDialogMsg {
|
pub struct NumberInputDialogMsg(pub u32);
|
||||||
Selected,
|
|
||||||
InfoRequested,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NumberInputDialog<F>
|
pub struct NumberInputDialog<F>
|
||||||
where
|
where
|
||||||
@ -31,8 +28,6 @@ where
|
|||||||
input: Child<NumberInput>,
|
input: Child<NumberInput>,
|
||||||
paragraphs: Child<Paragraphs<Paragraph<'static>>>,
|
paragraphs: Child<Paragraphs<Paragraph<'static>>>,
|
||||||
paragraphs_pad: Pad,
|
paragraphs_pad: Pad,
|
||||||
info_button: Child<Button>,
|
|
||||||
confirm_button: Child<Button>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> NumberInputDialog<F>
|
impl<F> NumberInputDialog<F>
|
||||||
@ -45,12 +40,9 @@ where
|
|||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
description_func,
|
description_func,
|
||||||
input: NumberInput::new(min, max, init_value).into_child(),
|
input: NumberInput::new(min, max, init_value).into_child(),
|
||||||
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(),
|
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, text))
|
||||||
paragraphs_pad: Pad::with_background(theme::BG),
|
|
||||||
info_button: Button::with_text(TR::buttons__info.into()).into_child(),
|
|
||||||
confirm_button: Button::with_text(TR::buttons__continue.into())
|
|
||||||
.styled(theme::button_confirm())
|
|
||||||
.into_child(),
|
.into_child(),
|
||||||
|
paragraphs_pad: Pad::with_background(theme::BG),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,23 +71,17 @@ where
|
|||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
self.area = bounds;
|
self.area = bounds;
|
||||||
let button_height = theme::BUTTON_HEIGHT;
|
let bot_padding = 20;
|
||||||
let content_area = self.area.inset(Insets::top(2 * theme::BUTTON_SPACING));
|
let top_padding = 14;
|
||||||
let (input_area, content_area) = content_area.split_top(button_height);
|
let button_height = theme::COUNTER_BUTTON_HEIGHT;
|
||||||
let (content_area, button_area) = content_area.split_bottom(button_height);
|
|
||||||
let content_area = content_area.inset(Insets::new(
|
let content_area = self.area.inset(Insets::top(top_padding));
|
||||||
theme::BUTTON_SPACING,
|
let (content_area, input_area) = content_area.split_bottom(button_height + bot_padding);
|
||||||
0,
|
let input_area = input_area.inset(Insets::bottom(bot_padding));
|
||||||
theme::BUTTON_SPACING,
|
|
||||||
theme::CONTENT_BORDER,
|
|
||||||
));
|
|
||||||
|
|
||||||
let grid = Grid::new(button_area, 1, 2).with_spacing(theme::KEYBOARD_SPACING);
|
|
||||||
self.input.place(input_area);
|
|
||||||
self.paragraphs.place(content_area);
|
self.paragraphs.place(content_area);
|
||||||
self.paragraphs_pad.place(content_area);
|
self.paragraphs_pad.place(content_area);
|
||||||
self.info_button.place(grid.row_col(0, 0));
|
self.input.place(input_area);
|
||||||
self.confirm_button.place(grid.row_col(0, 1));
|
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,29 +90,17 @@ where
|
|||||||
self.update_text(ctx, i);
|
self.update_text(ctx, i);
|
||||||
}
|
}
|
||||||
self.paragraphs.event(ctx, event);
|
self.paragraphs.event(ctx, event);
|
||||||
if let Some(ButtonMsg::Clicked) = self.info_button.event(ctx, event) {
|
|
||||||
return Some(Self::Msg::InfoRequested);
|
|
||||||
}
|
|
||||||
if let Some(ButtonMsg::Clicked) = self.confirm_button.event(ctx, event) {
|
|
||||||
return Some(Self::Msg::Selected);
|
|
||||||
};
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
self.input.paint();
|
todo!("remove when ui-t3t1 done");
|
||||||
self.paragraphs_pad.paint();
|
|
||||||
self.paragraphs.paint();
|
|
||||||
self.info_button.paint();
|
|
||||||
self.confirm_button.paint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
self.input.render(target);
|
self.input.render(target);
|
||||||
self.paragraphs_pad.render(target);
|
self.paragraphs_pad.render(target);
|
||||||
self.paragraphs.render(target);
|
self.paragraphs.render(target);
|
||||||
self.info_button.render(target);
|
|
||||||
self.confirm_button.render(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui_bounds")]
|
#[cfg(feature = "ui_bounds")]
|
||||||
@ -134,8 +108,26 @@ where
|
|||||||
sink(self.area);
|
sink(self.area);
|
||||||
self.input.bounds(sink);
|
self.input.bounds(sink);
|
||||||
self.paragraphs.bounds(sink);
|
self.paragraphs.bounds(sink);
|
||||||
self.info_button.bounds(sink);
|
}
|
||||||
self.confirm_button.bounds(sink);
|
}
|
||||||
|
|
||||||
|
impl<F> Swipable<NumberInputDialogMsg> for NumberInputDialog<F>
|
||||||
|
where
|
||||||
|
F: Fn(u32) -> TString<'static>,
|
||||||
|
{
|
||||||
|
fn swipe_start(
|
||||||
|
&mut self,
|
||||||
|
_ctx: &mut EventCtx,
|
||||||
|
direction: SwipeDirection,
|
||||||
|
) -> SwipableResult<NumberInputDialogMsg> {
|
||||||
|
match direction {
|
||||||
|
SwipeDirection::Up => SwipableResult::Return(NumberInputDialogMsg(self.value())),
|
||||||
|
_ => SwipableResult::Ignored,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swipe_finished(&self) -> bool {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,8 +140,6 @@ where
|
|||||||
t.component("NumberInputDialog");
|
t.component("NumberInputDialog");
|
||||||
t.child("input", &self.input);
|
t.child("input", &self.input);
|
||||||
t.child("paragraphs", &self.paragraphs);
|
t.child("paragraphs", &self.paragraphs);
|
||||||
t.child("info_button", &self.info_button);
|
|
||||||
t.child("confirm_button", &self.confirm_button);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,10 +158,10 @@ pub struct NumberInput {
|
|||||||
|
|
||||||
impl NumberInput {
|
impl NumberInput {
|
||||||
pub fn new(min: u32, max: u32, value: u32) -> Self {
|
pub fn new(min: u32, max: u32, value: u32) -> Self {
|
||||||
let dec = Button::with_text("-".into())
|
let dec = Button::with_icon(theme::ICON_MINUS)
|
||||||
.styled(theme::button_counter())
|
.styled(theme::button_counter())
|
||||||
.into_child();
|
.into_child();
|
||||||
let inc = Button::with_text("+".into())
|
let inc = Button::with_icon(theme::ICON_PLUS)
|
||||||
.styled(theme::button_counter())
|
.styled(theme::button_counter())
|
||||||
.into_child();
|
.into_child();
|
||||||
let value = value.clamp(min, max);
|
let value = value.clamp(min, max);
|
||||||
@ -219,21 +209,7 @@ impl Component for NumberInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
let mut buf = [0u8; 10];
|
todo!("remove when ui-t3t1 done");
|
||||||
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
|
||||||
let digit_font = Font::DEMIBOLD;
|
|
||||||
let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET.y;
|
|
||||||
display::rect_fill(self.area, theme::BG);
|
|
||||||
display::text_center(
|
|
||||||
self.area.center() + Offset::y(y_offset),
|
|
||||||
text,
|
|
||||||
digit_font,
|
|
||||||
theme::FG,
|
|
||||||
theme::BG,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.dec.paint();
|
|
||||||
self.inc.paint();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
@ -241,7 +217,7 @@ impl Component for NumberInput {
|
|||||||
|
|
||||||
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
||||||
let digit_font = Font::DEMIBOLD;
|
let digit_font = Font::DEMIBOLD;
|
||||||
let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET.y;
|
let y_offset = digit_font.text_height() / 2;
|
||||||
|
|
||||||
shape::Bar::new(self.area).with_bg(theme::BG).render(target);
|
shape::Bar::new(self.area).with_bg(theme::BG).render(target);
|
||||||
shape::Text::new(self.area.center() + Offset::y(y_offset), text)
|
shape::Text::new(self.area.center() + Offset::y(y_offset), text)
|
||||||
|
@ -6,6 +6,7 @@ pub mod confirm_set_new_pin;
|
|||||||
pub mod confirm_summary;
|
pub mod confirm_summary;
|
||||||
pub mod get_address;
|
pub mod get_address;
|
||||||
pub mod prompt_backup;
|
pub mod prompt_backup;
|
||||||
|
pub mod request_number;
|
||||||
pub mod show_share_words;
|
pub mod show_share_words;
|
||||||
pub mod warning_hi_prio;
|
pub mod warning_hi_prio;
|
||||||
|
|
||||||
@ -19,5 +20,6 @@ pub use confirm_set_new_pin::SetNewPin;
|
|||||||
pub use confirm_summary::new_confirm_summary;
|
pub use confirm_summary::new_confirm_summary;
|
||||||
pub use get_address::GetAddress;
|
pub use get_address::GetAddress;
|
||||||
pub use prompt_backup::PromptBackup;
|
pub use prompt_backup::PromptBackup;
|
||||||
|
pub use request_number::RequestNumber;
|
||||||
pub use show_share_words::ShowShareWords;
|
pub use show_share_words::ShowShareWords;
|
||||||
pub use warning_hi_prio::WarningHiPrio;
|
pub use warning_hi_prio::WarningHiPrio;
|
||||||
|
148
core/embed/rust/src/ui/model_mercury/flow/request_number.rs
Normal file
148
core/embed/rust/src/ui/model_mercury/flow/request_number.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use crate::{
|
||||||
|
error,
|
||||||
|
micropython::qstr::Qstr,
|
||||||
|
strutil::TString,
|
||||||
|
translations::TR,
|
||||||
|
ui::{
|
||||||
|
button_request::ButtonRequest,
|
||||||
|
component::{
|
||||||
|
text::paragraphs::{Paragraph, Paragraphs},
|
||||||
|
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||||
|
},
|
||||||
|
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{
|
||||||
|
component::{
|
||||||
|
CancelInfoConfirmMsg, Frame, FrameMsg, NumberInputDialog, NumberInputDialogMsg,
|
||||||
|
VerticalMenu, VerticalMenuChoiceMsg,
|
||||||
|
},
|
||||||
|
theme,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||||
|
pub enum RequestNumber {
|
||||||
|
Number,
|
||||||
|
Menu,
|
||||||
|
Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowState for RequestNumber {
|
||||||
|
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||||
|
match (self, direction) {
|
||||||
|
(RequestNumber::Number, SwipeDirection::Left) => {
|
||||||
|
Decision::Goto(RequestNumber::Menu, direction)
|
||||||
|
}
|
||||||
|
(RequestNumber::Menu, SwipeDirection::Right) => {
|
||||||
|
Decision::Goto(RequestNumber::Number, direction)
|
||||||
|
}
|
||||||
|
(RequestNumber::Info, SwipeDirection::Right) => {
|
||||||
|
Decision::Goto(RequestNumber::Menu, direction)
|
||||||
|
}
|
||||||
|
_ => Decision::Nothing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||||
|
match (self, msg) {
|
||||||
|
(RequestNumber::Number, FlowMsg::Info) => {
|
||||||
|
Decision::Goto(RequestNumber::Menu, SwipeDirection::Left)
|
||||||
|
}
|
||||||
|
(RequestNumber::Menu, FlowMsg::Choice(0)) => {
|
||||||
|
Decision::Goto(RequestNumber::Info, SwipeDirection::Left)
|
||||||
|
}
|
||||||
|
(RequestNumber::Menu, FlowMsg::Choice(1)) => Decision::Return(FlowMsg::Cancelled),
|
||||||
|
(RequestNumber::Menu, FlowMsg::Cancelled) => {
|
||||||
|
Decision::Goto(RequestNumber::Number, SwipeDirection::Right)
|
||||||
|
}
|
||||||
|
(RequestNumber::Info, FlowMsg::Cancelled) => {
|
||||||
|
Decision::Goto(RequestNumber::Menu, SwipeDirection::Right)
|
||||||
|
}
|
||||||
|
(RequestNumber::Number, FlowMsg::Choice(n)) => Decision::Return(FlowMsg::Choice(n)),
|
||||||
|
_ => Decision::Nothing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
micropython::{map::Map, obj::Obj, util},
|
||||||
|
ui::layout::obj::LayoutObj,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
pub extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
|
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, RequestNumber::new_obj) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestNumber {
|
||||||
|
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||||
|
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: Obj = kwargs.get(Qstr::MP_QSTR_description)?;
|
||||||
|
let info: Obj = kwargs.get(Qstr::MP_QSTR_info)?;
|
||||||
|
assert!(description != Obj::const_none());
|
||||||
|
assert!(info != Obj::const_none());
|
||||||
|
let br_type: TString = kwargs.get(Qstr::MP_QSTR_br_type)?.try_into()?;
|
||||||
|
let br_code: u16 = kwargs.get(Qstr::MP_QSTR_br_code)?.try_into()?;
|
||||||
|
|
||||||
|
let description_cb = move |i: u32| {
|
||||||
|
TString::try_from(
|
||||||
|
description
|
||||||
|
.call_with_n_args(&[i.try_into().unwrap()])
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let info_cb = move |i: u32| {
|
||||||
|
TString::try_from(info.call_with_n_args(&[i.try_into().unwrap()]).unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let number_input_dialog =
|
||||||
|
NumberInputDialog::new(min_count, max_count, count, description_cb)?;
|
||||||
|
let content_number_input = Frame::left_aligned(title, number_input_dialog)
|
||||||
|
.with_menu_button()
|
||||||
|
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||||
|
.map(|msg| match msg {
|
||||||
|
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||||
|
FrameMsg::Content(NumberInputDialogMsg(n)) => Some(FlowMsg::Choice(n as usize)),
|
||||||
|
})
|
||||||
|
.one_button_request(ButtonRequest::from_tstring(br_code, br_type));
|
||||||
|
|
||||||
|
let content_menu = Frame::left_aligned(
|
||||||
|
"".into(),
|
||||||
|
VerticalMenu::empty()
|
||||||
|
.item(theme::ICON_CHEVRON_RIGHT, TR::buttons__more_info.into())
|
||||||
|
.danger(theme::ICON_CANCEL, TR::backup__title_skip.into()),
|
||||||
|
)
|
||||||
|
.with_cancel_button()
|
||||||
|
.map(|msg| match msg {
|
||||||
|
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||||
|
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||||
|
FrameMsg::Button(_) => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let paragraphs_info = Paragraphs::new(Paragraph::new(
|
||||||
|
&theme::TEXT_MAIN_GREY_LIGHT,
|
||||||
|
info_cb(0), // TODO: get the value
|
||||||
|
));
|
||||||
|
let content_info = Frame::left_aligned(
|
||||||
|
TR::backup__title_skip.into(),
|
||||||
|
SwipePage::vertical(paragraphs_info),
|
||||||
|
)
|
||||||
|
.with_cancel_button()
|
||||||
|
.map(|msg| match msg {
|
||||||
|
FrameMsg::Button(CancelInfoConfirmMsg::Cancelled) => Some(FlowMsg::Cancelled),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let store = flow_store()
|
||||||
|
.add(content_number_input)?
|
||||||
|
.add(content_menu)?
|
||||||
|
.add(content_info)?;
|
||||||
|
let res = SwipeFlow::new(RequestNumber::Number, store)?;
|
||||||
|
Ok(LayoutObj::new(res)?.into())
|
||||||
|
}
|
||||||
|
}
|
@ -41,10 +41,10 @@ use super::{
|
|||||||
AddressDetails, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
|
AddressDetails, Bip39Input, Button, ButtonMsg, ButtonPage, ButtonStyleSheet,
|
||||||
CancelConfirmMsg, CancelInfoConfirmMsg, CoinJoinProgress, Dialog, DialogMsg, FidoConfirm,
|
CancelConfirmMsg, CancelInfoConfirmMsg, CoinJoinProgress, Dialog, DialogMsg, FidoConfirm,
|
||||||
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
|
FidoMsg, Frame, FrameMsg, Homescreen, HomescreenMsg, IconDialog, Lockscreen, MnemonicInput,
|
||||||
MnemonicKeyboard, MnemonicKeyboardMsg, NumberInputDialog, NumberInputDialogMsg,
|
MnemonicKeyboard, MnemonicKeyboardMsg, PassphraseKeyboard, PassphraseKeyboardMsg,
|
||||||
PassphraseKeyboard, PassphraseKeyboardMsg, PinKeyboard, PinKeyboardMsg, Progress,
|
PinKeyboard, PinKeyboardMsg, Progress, PromptScreen, SelectWordCount, SelectWordCountMsg,
|
||||||
PromptScreen, SelectWordCount, SelectWordCountMsg, SimplePage, Slip39Input, StatusScreen,
|
SimplePage, Slip39Input, StatusScreen, SwipeUpScreen, SwipeUpScreenMsg, VerticalMenu,
|
||||||
SwipeUpScreen, SwipeUpScreenMsg, VerticalMenu, VerticalMenuChoiceMsg,
|
VerticalMenuChoiceMsg,
|
||||||
},
|
},
|
||||||
flow, theme,
|
flow, theme,
|
||||||
};
|
};
|
||||||
@ -247,19 +247,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> ComponentMsgObj for NumberInputDialog<F>
|
|
||||||
where
|
|
||||||
F: Fn(u32) -> TString<'static>,
|
|
||||||
{
|
|
||||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
|
||||||
let value = self.value().try_into()?;
|
|
||||||
match msg {
|
|
||||||
NumberInputDialogMsg::Selected => Ok((CONFIRMED.as_obj(), value).try_into()?),
|
|
||||||
NumberInputDialogMsg::InfoRequested => Ok((CANCELLED.as_obj(), value).try_into()?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComponentMsgObj for Progress {
|
impl ComponentMsgObj for Progress {
|
||||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -1175,33 +1162,6 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
|||||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
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(
|
|
||||||
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 {
|
extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||||
@ -1860,16 +1820,20 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// confirmation."""
|
/// confirmation."""
|
||||||
Qstr::MP_QSTR_flow_show_share_words => obj_fn_kw!(0, flow::show_share_words::new_show_share_words).as_obj(),
|
Qstr::MP_QSTR_flow_show_share_words => obj_fn_kw!(0, flow::show_share_words::new_show_share_words).as_obj(),
|
||||||
|
|
||||||
/// def request_number(
|
/// def flow_request_number(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str,
|
/// title: str,
|
||||||
/// count: int,
|
/// count: int,
|
||||||
/// min_count: int,
|
/// min_count: int,
|
||||||
/// max_count: int,
|
/// max_count: int,
|
||||||
/// description: Callable[[int], str] | None = None,
|
/// description: Callable[[int], str] | None = None,
|
||||||
|
/// info: Callable[[int], str] | None = None,
|
||||||
|
/// br_code: ButtonRequestType,
|
||||||
|
/// br_type: str,
|
||||||
/// ) -> LayoutObj[tuple[UiResult, int]]:
|
/// ) -> LayoutObj[tuple[UiResult, int]]:
|
||||||
/// """Number input with + and - buttons, description, and info button."""
|
/// """Numer input with + and - buttons, description, and context menu with cancel and
|
||||||
Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(),
|
/// info."""
|
||||||
|
Qstr::MP_QSTR_flow_request_number => obj_fn_kw!(0, flow::request_number::new_request_number).as_obj(),
|
||||||
|
|
||||||
/// def show_checklist(
|
/// def show_checklist(
|
||||||
/// *,
|
/// *,
|
||||||
|
@ -416,6 +416,22 @@ def request_number(
|
|||||||
"""Number input with + and - buttons, description, and info button."""
|
"""Number input with + and - buttons, description, and info button."""
|
||||||
|
|
||||||
|
|
||||||
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
|
def flow_request_number(
|
||||||
|
*,
|
||||||
|
title: str,
|
||||||
|
count: int,
|
||||||
|
min_count: int,
|
||||||
|
max_count: int,
|
||||||
|
description: Callable[[int], str] | None = None,
|
||||||
|
info: Callable[[int], str] | None = None,
|
||||||
|
br_code: ButtonRequestType,
|
||||||
|
br_type: str,
|
||||||
|
) -> LayoutObj[tuple[UiResult, int]]:
|
||||||
|
"""Numer input with + and - buttons, description, and context menu with cancel and
|
||||||
|
info."""
|
||||||
|
|
||||||
|
|
||||||
# rust/src/ui/model_mercury/layout.rs
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
def show_checklist(
|
def show_checklist(
|
||||||
*,
|
*,
|
||||||
|
@ -134,41 +134,32 @@ async def _prompt_number(
|
|||||||
max_count: int,
|
max_count: int,
|
||||||
br_name: str,
|
br_name: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
num_input = RustLayout(
|
|
||||||
trezorui2.request_number(
|
result = await RustLayout(
|
||||||
|
trezorui2.flow_request_number(
|
||||||
title=title,
|
title=title,
|
||||||
description=description,
|
description=description,
|
||||||
count=count,
|
count=count,
|
||||||
min_count=min_count,
|
min_count=min_count,
|
||||||
max_count=max_count,
|
max_count=max_count,
|
||||||
|
info=info,
|
||||||
|
br_code=ButtonRequestType.ResetDevice,
|
||||||
|
br_type=br_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
while True:
|
|
||||||
result = await interact(
|
|
||||||
num_input,
|
|
||||||
br_name,
|
|
||||||
ButtonRequestType.ResetDevice,
|
|
||||||
)
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
|
# TODO: is this still relevant?
|
||||||
if not isinstance(result, tuple):
|
if not isinstance(result, tuple):
|
||||||
# DebugLink currently can't send number of shares and it doesn't
|
# DebugLink currently can't send number of shares and it doesn't
|
||||||
# change the counter either so just use the initial value.
|
# change the counter either so just use the initial value.
|
||||||
result = (result, count)
|
result = (result, count)
|
||||||
status, value = result
|
status, value = result
|
||||||
|
|
||||||
if status == CONFIRMED:
|
if status == CONFIRMED:
|
||||||
assert isinstance(value, int)
|
assert isinstance(value, int)
|
||||||
return value
|
return value
|
||||||
|
else:
|
||||||
await RustLayout(
|
raise ActionCancelled # user cancelled request number prompt
|
||||||
trezorui2.show_simple(
|
|
||||||
title=None,
|
|
||||||
description=info(value),
|
|
||||||
button=TR.buttons__ok_i_understand,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
num_input.request_complete_repaint()
|
|
||||||
|
|
||||||
|
|
||||||
async def slip39_prompt_threshold(
|
async def slip39_prompt_threshold(
|
||||||
|
Loading…
Reference in New Issue
Block a user