1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-25 17:09:44 +00:00

refactor(core): move show_success/warning/error

- return type is Gc<LayoutObj>
- new_show_modal removed from model_t layout.rs
This commit is contained in:
obrusvit 2024-11-05 09:41:43 +01:00
parent e551a61e39
commit 84ca196f35
18 changed files with 403 additions and 412 deletions

View File

@ -741,7 +741,6 @@ static void _librust_qstrs(void) {
MP_QSTR_verb_info;
MP_QSTR_verify;
MP_QSTR_version;
MP_QSTR_warning;
MP_QSTR_wipe__info;
MP_QSTR_wipe__title;
MP_QSTR_wipe__want_to_wipe;

View File

@ -299,6 +299,20 @@ extern "C" fn new_show_checklist(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_show_error(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 description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
let layout = ModelUI::show_error(title, button, description, allow_cancel, time_ms)?;
Ok(layout.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_group_share_success(
n_args: usize,
args: *const Obj,
@ -418,6 +432,20 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_success(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 description: TString = kwargs.get_or(Qstr::MP_QSTR_description, TString::empty())?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, false)?;
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
let layout = ModelUI::show_success(title, button, description, allow_cancel, time_ms)?;
Ok(layout.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_wait_text(message: Obj) -> Obj {
let block = || {
let message: TString<'static> = message.try_into()?;
@ -429,6 +457,30 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
unsafe { util::try_or_raise(block) }
}
extern "C" fn new_show_warning(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 value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
let danger: bool = kwargs.get_or(Qstr::MP_QSTR_danger, false)?;
let layout = ModelUI::show_warning(
title,
button,
value,
description,
allow_cancel,
time_ms,
danger,
)?;
Ok(layout.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], _kwargs: &Map| {
let layout = ModelUI::tutorial()?;
@ -700,6 +752,17 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// mark next to them. Limited to 3 items."""
Qstr::MP_QSTR_show_checklist => obj_fn_kw!(0, new_show_checklist).as_obj(),
/// def show_error(
/// *,
/// title: str,
/// button: str,
/// description: str = "",
/// allow_cancel: bool = True,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Error modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_error => obj_fn_kw!(0, new_show_error).as_obj(),
/// def show_group_share_success(
/// *,
/// lines: Iterable[str],
@ -771,10 +834,34 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// """Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
Qstr::MP_QSTR_show_remaining_shares => obj_fn_kw!(0, new_show_remaining_shares).as_obj(),
/// def show_success(
/// *,
/// title: str,
/// button: str,
/// description: str = "",
/// allow_cancel: bool = True,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Success modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
/// def show_wait_text(message: str, /) -> LayoutObj[None]:
/// """Show single-line text in the middle of the screen."""
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),
/// def show_warning(
/// *,
/// title: str,
/// button: str,
/// value: str = "",
/// description: str = "",
/// allow_cancel: bool = True,
/// time_ms: int = 0,
/// danger: bool = False, # unused on TT
/// ) -> LayoutObj[UiResult]:
/// """Warning modal. TT: No buttons shown when `button` is empty string. TR: middle button and centered text."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
/// def tutorial() -> LayoutObj[UiResult]:
/// """Show user how to interact with the device."""
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, new_tutorial).as_obj(),

View File

@ -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_show_error(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 description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let allow_cancel: bool = kwargs.get(Qstr::MP_QSTR_allow_cancel)?.try_into()?;
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
let frame = if allow_cancel {
Frame::left_aligned(title, SwipeContent::new(content))
.with_cancel_button()
.with_danger()
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(Direction::Up, SwipeSettings::default())
} else {
Frame::left_aligned(title, SwipeContent::new(content))
.with_danger()
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(Direction::Up, SwipeSettings::default())
};
let frame = SwipeUpScreen::new(frame);
Ok(LayoutObj::new(frame)?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_share_words(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()?;
@ -716,58 +690,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_show_warning(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 description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
let action: Option<TString> = kwargs.get(Qstr::MP_QSTR_button)?.try_into_option()?;
let danger: bool = kwargs.get_or(Qstr::MP_QSTR_danger, false)?;
let content = ParagraphVecShort::from_iter([
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description),
Paragraph::new(&theme::TEXT_MAIN_GREY_EXTRA_LIGHT, value),
])
.into_paragraphs();
let frame = Frame::left_aligned(title, SwipeContent::new(content))
.with_footer(TR::instructions__swipe_up.into(), action)
.with_swipe(Direction::Up, SwipeSettings::default());
let frame_with_icon = if danger {
frame.with_danger_icon()
} else {
frame.with_warning_low_icon()
};
Ok(LayoutObj::new(SwipeUpScreen::new(frame_with_icon))?.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_success(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 description: Option<TString> = kwargs
.get(Qstr::MP_QSTR_description)?
.try_into_option()?
.and_then(|desc: TString| if desc.is_empty() { None } else { Some(desc) });
let content = StatusScreen::new_success(title);
let obj = LayoutObj::new(SwipeUpScreen::new(
Frame::left_aligned(
TR::words__title_success.into(),
SwipeContent::new(content).with_no_attach_anim(),
)
.with_footer(TR::instructions__swipe_up.into(), description)
.with_result_icon(ICON_BULLET_CHECKMARK, theme::GREEN_LIGHT)
.with_swipe(Direction::Up, SwipeSettings::default()),
))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
@ -1051,41 +973,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def show_error(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Error modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_error => obj_fn_kw!(0, new_show_error).as_obj(),
/// def show_warning(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// value: str = "",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// danger: bool = False,
/// ) -> LayoutObj[UiResult]:
/// """Warning modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
/// def show_success(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Success screen. Description is used in the footer."""
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
/// def show_simple(
/// *,
/// title: str | None,

View File

@ -8,10 +8,13 @@ use crate::{
translations::TR,
ui::{
component::{
connect::Connect, swipe_detect::SwipeSettings, text::paragraphs::{
connect::Connect,
swipe_detect::SwipeSettings,
text::paragraphs::{
Checklist, Paragraph, ParagraphSource, ParagraphVecLong, ParagraphVecShort,
Paragraphs, VecExt,
}, CachedJpeg, ComponentExt, Empty, Never, Timeout
},
CachedJpeg, ComponentExt, Empty, Never, Timeout,
},
geometry::{self, Direction},
layout::{
@ -25,8 +28,8 @@ use crate::{
use super::{
component::{
check_homescreen_format, Bip39Input, CoinJoinProgress, Frame, Homescreen, Lockscreen,
MnemonicKeyboard, PinKeyboard, Progress, SelectWordCount, Slip39Input, SwipeContent,
SwipeUpScreen, VerticalMenu,
MnemonicKeyboard, PinKeyboard, Progress, SelectWordCount, Slip39Input, StatusScreen,
SwipeContent, SwipeUpScreen, VerticalMenu,
},
flow::{self, new_confirm_action_simple, ConfirmActionMenu, ConfirmActionStrings},
theme, ModelMercuryFeatures,
@ -356,6 +359,31 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
Ok(layout)
}
fn show_error(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
let content = Paragraphs::new(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
let frame = if allow_cancel {
Frame::left_aligned(title, SwipeContent::new(content))
.with_cancel_button()
.with_danger()
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(Direction::Up, SwipeSettings::default())
} else {
Frame::left_aligned(title, SwipeContent::new(content))
.with_danger()
.with_footer(TR::instructions__swipe_up.into(), None)
.with_swipe(Direction::Up, SwipeSettings::default())
};
let obj = LayoutObj::new(SwipeUpScreen::new(frame))?;
Ok(obj)
}
fn show_group_share_success(
lines: [TString<'static>; 4],
) -> Result<impl LayoutMaybeTrace, Error> {
@ -475,11 +503,70 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
))
}
fn show_success(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
// description used in the Footer
let description = if description.is_empty() {
None
} else {
Some(description)
};
let content = StatusScreen::new_success(title);
let layout = LayoutObj::new(SwipeUpScreen::new(
Frame::left_aligned(
TR::words__title_success.into(),
SwipeContent::new(content).with_no_attach_anim(),
)
.with_footer(TR::instructions__swipe_up.into(), description)
.with_result_icon(theme::ICON_BULLET_CHECKMARK, theme::GREEN_LIGHT)
.with_swipe(Direction::Up, SwipeSettings::default()),
))?;
Ok(layout)
}
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error> {
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
Ok(layout)
}
fn show_warning(
title: TString<'static>,
button: TString<'static>,
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
let action = if button.is_empty() {
None
} else {
Some(button)
};
let content = ParagraphVecShort::from_iter([
Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description),
Paragraph::new(&theme::TEXT_MAIN_GREY_EXTRA_LIGHT, value),
])
.into_paragraphs();
let frame = Frame::left_aligned(title, SwipeContent::new(content))
.with_footer(TR::instructions__swipe_up.into(), action)
.with_swipe(Direction::Up, SwipeSettings::default());
let frame_with_icon = if danger {
frame.with_danger_icon()
} else {
frame.with_warning_low_icon()
};
Ok(LayoutObj::new(SwipeUpScreen::new(frame_with_icon))?)
}
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
let flow = flow::show_tutorial::new_show_tutorial()?;
Ok(flow)

View File

@ -842,38 +842,6 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_warning(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()?;
let warning: TString = kwargs.get(Qstr::MP_QSTR_warning)?.try_into()?;
let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::none_armed_none(button);
let btn_actions = ButtonActions::none_confirm_none();
let mut ops = OpTextLayout::new(theme::TEXT_NORMAL);
ops = ops.alignment(geometry::Alignment::Center);
if !warning.is_empty() {
ops = ops.text_bold_upper(warning);
if !description.is_empty() {
ops = ops.newline();
}
}
if !description.is_empty() {
ops = ops.text_normal(description);
}
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, 1);
let obj = LayoutObj::new(Flow::new(pages))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_passphrase() -> Obj {
let block = move || {
let text: TString = TR::passphrase__please_enter.into();
@ -1164,15 +1132,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 show_warning(
/// *,
/// button: str,
/// warning: str,
/// description: str,
/// ) -> LayoutObj[UiResult]:
/// """Warning modal with middle button and centered text."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
/// def show_passphrase() -> LayoutObj[UiResult]:
/// """Show passphrase on host dialog."""
Qstr::MP_QSTR_show_passphrase => obj_fn_0!(new_show_passphrase).as_obj(),

View File

@ -19,6 +19,7 @@ use crate::{
},
Component, ComponentExt, Empty, FormattedText, Label, LineBreaking, Paginate, Timeout,
},
geometry,
layout::{
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
util::RecoveryType,
@ -375,6 +376,16 @@ impl UIFeaturesFirmware for ModelTRFeatures {
Ok(layout)
}
fn show_error(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
Err::<Gc<LayoutObj>, Error>(Error::ValueError(c"show error not supported"))
}
fn show_group_share_success(
lines: [TString<'static>; 4],
) -> Result<impl LayoutMaybeTrace, Error> {
@ -499,11 +510,54 @@ impl UIFeaturesFirmware for ModelTRFeatures {
))
}
fn show_success(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
Err::<Gc<LayoutObj>, Error>(Error::ValueError(c"show success not supported"))
}
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error> {
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
Ok(layout)
}
fn show_warning(
title: TString<'static>,
button: TString<'static>,
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
let get_page = move |page_index| {
assert!(page_index == 0);
let btn_layout = ButtonLayout::none_armed_none(button);
let btn_actions = ButtonActions::none_confirm_none();
let mut ops = OpTextLayout::new(theme::TEXT_NORMAL);
ops = ops.alignment(geometry::Alignment::Center);
if !value.is_empty() {
ops = ops.text_bold_upper(value);
if !description.is_empty() {
ops = ops.newline();
}
}
if !description.is_empty() {
ops = ops.text_normal(description);
}
let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted)
};
let pages = FlowPages::new(get_page, 1);
let obj = LayoutObj::new(Flow::new(pages))?;
Ok(obj)
}
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
const PAGE_COUNT: usize = 7;

View File

@ -702,89 +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) }
}
fn new_show_modal(
kwargs: &Map,
icon: BlendedImage,
button_style: ButtonStyleSheet,
) -> Result<Obj, Error> {
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let value: TString = kwargs.get_or(Qstr::MP_QSTR_value, "".into())?;
let description: TString = kwargs.get_or(Qstr::MP_QSTR_description, "".into())?;
let button: TString = kwargs.get_or(Qstr::MP_QSTR_button, TR::buttons__continue.into())?;
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
let no_buttons = button.is_empty();
let obj = if no_buttons && time_ms == 0 {
// No buttons and no timer, used when we only want to draw the dialog once and
// then throw away the layout object.
LayoutObj::new(
IconDialog::new(icon, title, Empty)
.with_value(value)
.with_description(description),
)?
.into()
} else if no_buttons && time_ms > 0 {
// Timeout, no buttons.
LayoutObj::new(
IconDialog::new(
icon,
title,
Timeout::new(time_ms).map(|_| Some(CancelConfirmMsg::Confirmed)),
)
.with_value(value)
.with_description(description),
)?
.into()
} else if allow_cancel {
// Two buttons.
LayoutObj::new(
IconDialog::new(
icon,
title,
Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL),
Button::with_text(button).styled(button_style),
false,
),
)
.with_value(value)
.with_description(description),
)?
.into()
} else {
// Single button.
LayoutObj::new(
IconDialog::new(
icon,
title,
theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})),
)
.with_value(value)
.with_description(description),
)?
.into()
};
Ok(obj)
}
extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let icon = BlendedImage::new(
theme::IMAGE_BG_CIRCLE,
theme::IMAGE_FG_ERROR,
theme::ERROR_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(kwargs, icon, theme::button_default())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_confirm_fido(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()?;
@ -816,34 +733,6 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let icon = BlendedImage::new(
theme::IMAGE_BG_OCTAGON,
theme::IMAGE_FG_WARN,
theme::WARN_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(kwargs, icon, theme::button_reset())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let icon = BlendedImage::new(
theme::IMAGE_BG_CIRCLE,
theme::IMAGE_FG_SUCCESS,
theme::SUCCESS_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(kwargs, icon, theme::button_confirm())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
}
extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: Option<TString> = kwargs.get(Qstr::MP_QSTR_title)?.try_into_option()?;
@ -1144,41 +1033,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """
Qstr::MP_QSTR_confirm_fido => obj_fn_kw!(0, new_confirm_fido).as_obj(),
/// def show_error(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Error modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_error => obj_fn_kw!(0, new_show_error).as_obj(),
/// def show_warning(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// value: str = "",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// danger: bool = False, # unused on TT
/// ) -> LayoutObj[UiResult]:
/// """Warning modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_warning => obj_fn_kw!(0, new_show_warning).as_obj(),
/// def show_success(
/// *,
/// title: str,
/// button: str = "CONTINUE",
/// description: str = "",
/// allow_cancel: bool = False,
/// time_ms: int = 0,
/// ) -> LayoutObj[UiResult]:
/// """Success modal. No buttons shown when `button` is empty string."""
Qstr::MP_QSTR_show_success => obj_fn_kw!(0, new_show_success).as_obj(),
/// def show_simple(
/// *,
/// title: str | None,

View File

@ -406,6 +406,32 @@ impl UIFeaturesFirmware for ModelTTFeatures {
Ok(layout)
}
fn show_error(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
let icon = BlendedImage::new(
theme::IMAGE_BG_CIRCLE,
theme::IMAGE_FG_ERROR,
theme::ERROR_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(
title,
TString::empty(),
description,
button,
allow_cancel,
time_ms,
icon,
theme::button_default(),
)
}
fn show_group_share_success(
lines: [TString<'static>; 4],
) -> Result<impl LayoutMaybeTrace, Error> {
@ -447,7 +473,7 @@ impl UIFeaturesFirmware for ModelTTFeatures {
theme::FG,
theme::BG,
);
let obj = new_show_modal(
new_show_modal(
title,
TString::empty(),
description,
@ -456,8 +482,7 @@ impl UIFeaturesFirmware for ModelTTFeatures {
time_ms,
icon,
theme::button_info(),
)?;
Ok(obj)
)
}
fn show_lockscreen(
@ -562,11 +587,65 @@ impl UIFeaturesFirmware for ModelTTFeatures {
Ok(layout)
}
fn show_success(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error> {
let icon = BlendedImage::new(
theme::IMAGE_BG_CIRCLE,
theme::IMAGE_FG_SUCCESS,
theme::SUCCESS_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(
title,
TString::empty(),
description,
button,
allow_cancel,
time_ms,
icon,
theme::button_confirm(),
)
}
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error> {
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
Ok(layout)
}
fn show_warning(
title: TString<'static>,
button: TString<'static>,
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
danger: bool,
) -> Result<Gc<LayoutObj>, Error> {
let icon = BlendedImage::new(
theme::IMAGE_BG_OCTAGON,
theme::IMAGE_FG_WARN,
theme::WARN_COLOR,
theme::FG,
theme::BG,
);
new_show_modal(
title,
value,
description,
button,
allow_cancel,
0,
icon,
theme::button_reset(),
)
}
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
Err::<RootComponent<Empty, ModelTTFeatures>, Error>(Error::ValueError(
c"tutorial not supported",

View File

@ -1,4 +1,9 @@
use crate::{error::Error, io::BinaryData, micropython::{gc::Gc, obj::Obj}, strutil::TString};
use crate::{
error::Error,
io::BinaryData,
micropython::{gc::Gc, obj::Obj},
strutil::TString,
};
use super::layout::{
obj::{LayoutMaybeTrace, LayoutObj},
@ -103,6 +108,14 @@ pub trait UIFeaturesFirmware {
items: [TString<'static>; 3],
) -> Result<impl LayoutMaybeTrace, Error>;
fn show_error(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
fn show_group_share_success(
lines: [TString<'static>; 4],
) -> Result<impl LayoutMaybeTrace, Error>;
@ -146,7 +159,25 @@ pub trait UIFeaturesFirmware {
pages_iterable: Obj, // TODO: replace Obj
) -> Result<impl LayoutMaybeTrace, Error>;
fn show_success(
title: TString<'static>,
button: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error>;
fn show_warning(
title: TString<'static>,
button: TString<'static>,
value: TString<'static>,
description: TString<'static>,
allow_cancel: bool,
time_ms: u32,
danger: bool,
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
fn tutorial() -> Result<impl LayoutMaybeTrace, Error>;
}

View File

@ -121,44 +121,6 @@ def confirm_fido(
"""
# rust/src/ui/model_mercury/layout.rs
def show_error(
*,
title: str,
button: str = "CONTINUE",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Error modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_mercury/layout.rs
def show_warning(
*,
title: str,
button: str = "CONTINUE",
value: str = "",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
danger: bool = False,
) -> LayoutObj[UiResult]:
"""Warning modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_mercury/layout.rs
def show_success(
*,
title: str,
button: str = "CONTINUE",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Success screen. Description is used in the footer."""
# rust/src/ui/model_mercury/layout.rs
def show_simple(
*,
@ -450,16 +412,6 @@ def multiple_pages_texts(
"""Show multiple texts, each on its own page."""
# rust/src/ui/model_tr/layout.rs
def show_warning(
*,
button: str,
warning: str,
description: str,
) -> LayoutObj[UiResult]:
"""Warning modal with middle button and centered text."""
# rust/src/ui/model_tr/layout.rs
def show_passphrase() -> LayoutObj[UiResult]:
"""Show passphrase on host dialog."""
@ -635,44 +587,6 @@ def confirm_fido(
"""
# rust/src/ui/model_tt/layout.rs
def show_error(
*,
title: str,
button: str = "CONTINUE",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Error modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_warning(
*,
title: str,
button: str = "CONTINUE",
value: str = "",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
danger: bool = False, # unused on TT
) -> LayoutObj[UiResult]:
"""Warning modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_success(
*,
title: str,
button: str = "CONTINUE",
description: str = "",
allow_cancel: bool = False,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Success modal. No buttons shown when `button` is empty string."""
# rust/src/ui/model_tt/layout.rs
def show_simple(
*,

View File

@ -244,6 +244,18 @@ def show_checklist(
mark next to them. Limited to 3 items."""
# rust/src/ui/api/firmware_upy.rs
def show_error(
*,
title: str,
button: str,
description: str = "",
allow_cancel: bool = True,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Error modal. No buttons shown when `button` is empty string."""
# rust/src/ui/api/firmware_upy.rs
def show_group_share_success(
*,
@ -323,11 +335,37 @@ def show_remaining_shares(
"""Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
# rust/src/ui/api/firmware_upy.rs
def show_success(
*,
title: str,
button: str,
description: str = "",
allow_cancel: bool = True,
time_ms: int = 0,
) -> LayoutObj[UiResult]:
"""Success modal. No buttons shown when `button` is empty string."""
# rust/src/ui/api/firmware_upy.rs
def show_wait_text(message: str, /) -> LayoutObj[None]:
"""Show single-line text in the middle of the screen."""
# rust/src/ui/api/firmware_upy.rs
def show_warning(
*,
title: str,
button: str,
value: str = "",
description: str = "",
allow_cancel: bool = True,
time_ms: int = 0,
danger: bool = False, # unused on TT
) -> LayoutObj[UiResult]:
"""Warning modal. TT: No buttons shown when `button` is empty string. TR: middle button and centered text."""
# rust/src/ui/api/firmware_upy.rs
def tutorial() -> LayoutObj[UiResult]:
"""Show user how to interact with the device."""

View File

@ -97,7 +97,7 @@ def confirm_reset_device(recovery: bool = False) -> Awaitable[None]:
async def show_wallet_created_success() -> None:
await interact(
trezorui2.show_success(title=TR.backup__new_wallet_created, description=""),
trezorui_api.show_success(title=TR.backup__new_wallet_created, button=""),
"backup_device",
ButtonRequestType.ResetDevice,
)
@ -286,7 +286,7 @@ async def show_error_and_raise(
) -> NoReturn:
button = button or TR.buttons__try_again # def_arg
await interact(
trezorui2.show_error(
trezorui_api.show_error(
title=subheader or "",
description=content,
button=button,
@ -308,7 +308,7 @@ def show_warning(
) -> Awaitable[None]:
button = button or TR.buttons__continue # def_arg
return raise_if_not_confirmed(
trezorui2.show_warning(
trezorui_api.show_warning(
title=TR.words__important,
value=content,
button=subheader or TR.words__continue_anyway_question,
@ -326,8 +326,9 @@ def show_success(
button: str | None = None,
) -> Awaitable[None]:
return raise_if_not_confirmed(
trezorui2.show_success(
trezorui_api.show_success(
title=content,
button="",
description=subheader if subheader else "",
),
br_name,
@ -1073,7 +1074,7 @@ def error_popup(
if subtitle:
title += f"\n{subtitle}"
return trezorui2.show_error(
return trezorui_api.show_error(
title=title,
description=description.format(description_param),
button=button,

View File

@ -135,7 +135,7 @@ async def show_recovery_warning(
) -> None:
button = button or TR.buttons__try_again # def_arg
await raise_if_not_confirmed(
trezorui2.show_warning(
trezorui_api.show_warning(
title=content or TR.words__warning,
value=subheader or "",
button=button,

View File

@ -301,7 +301,7 @@ async def show_intro_backup(single_share: bool, num_of_words: int | None) -> Non
def show_warning_backup() -> Awaitable[ui.UiResult]:
return interact(
trezorui2.show_warning(
trezorui_api.show_warning(
title=TR.words__important,
value=TR.reset__never_make_digital_copy,
button="",
@ -328,7 +328,7 @@ def show_reset_warning(
br_code: ButtonRequestType = ButtonRequestType.Warning,
) -> Awaitable[None]:
return raise_if_not_confirmed(
trezorui2.show_warning(
trezorui_api.show_warning(
title=subheader or "",
description=content,
value="",

View File

@ -393,9 +393,10 @@ def show_warning(
content = content + "\n"
return interact(
trezorui2.show_warning( # type: ignore [Argument missing for parameter "title"]
trezorui_api.show_warning( # type: ignore [Argument missing for parameter "title"]
title="",
button=button,
warning=content, # type: ignore [No parameter named "warning"]
value=content,
description=subheader or "",
),
br_name,

View File

@ -136,7 +136,7 @@ def confirm_path_warning(path: str, path_type: str | None = None) -> Awaitable[N
else f"{TR.words__unknown} {path_type.lower()}."
)
return raise_if_not_confirmed(
trezorui2.show_warning(
trezorui_api.show_warning(
title=title,
value=path,
description=TR.words__continue_anyway_question,
@ -323,7 +323,7 @@ async def show_error_and_raise(
) -> NoReturn:
button = button or TR.buttons__try_again # def_arg
await interact(
trezorui2.show_error(
trezorui_api.show_error(
title=subheader or "",
description=content,
button=button,
@ -347,7 +347,7 @@ def show_warning(
) -> Awaitable[None]:
button = button or TR.buttons__continue # def_arg
return raise_if_not_confirmed(
trezorui2.show_warning(
trezorui_api.show_warning(
title=content,
description=subheader or "",
button=button,
@ -365,7 +365,7 @@ def show_success(
) -> Awaitable[None]:
button = button or TR.buttons__continue # def_arg
return raise_if_not_confirmed(
trezorui2.show_success(
trezorui_api.show_success(
title=content,
description=subheader or "",
button=button,
@ -1121,7 +1121,7 @@ def error_popup(
if subtitle:
title += f"\n{subtitle}"
layout = trezorui2.show_error(
layout = trezorui_api.show_error(
title=title,
description=description.format(description_param),
button=button,

View File

@ -185,7 +185,7 @@ def show_recovery_warning(
button = button or TR.buttons__try_again # def_arg
return interact(
trezorui2.show_warning(
trezorui_api.show_warning(
title=content,
description=subheader or "",
button=button,

View File

@ -355,7 +355,7 @@ def show_reset_warning(
) -> Awaitable[trezorui_api.UiResult]:
button = button or TR.buttons__try_again # def_arg
return interact(
trezorui2.show_warning(
trezorui_api.show_warning(
title=subheader or "",
description=content,
button=button,