1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-22 06:28:13 +00:00

refactor(delizia): use external menu on confirm_action

[no changelog]
This commit is contained in:
Ioan Bizău 2025-07-07 11:41:02 +02:00 committed by Ioan Bizău
parent 1e7108f6d2
commit 566f6fe7cb
9 changed files with 72 additions and 22 deletions

View File

@ -71,6 +71,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
.get(Qstr::MP_QSTR_prompt_title)
.unwrap_or_else(|_| Obj::const_none())
.try_into_option()?;
let external_menu: bool = kwargs.get_or(Qstr::MP_QSTR_external_menu, false)?;
let layout = ModelUI::confirm_action(
title,
@ -84,6 +85,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
reverse,
prompt_screen,
prompt_title,
external_menu,
)?;
Ok(LayoutObj::new_root(layout)?.into())
};
@ -1351,6 +1353,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// reverse: bool = False,
/// prompt_screen: bool = False,
/// prompt_title: str | None = None,
/// external_menu: bool = False,
/// ) -> LayoutObj[UiResult]:
/// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),

View File

@ -57,6 +57,7 @@ impl FirmwareUI for UIBolt {
reverse: bool,
_prompt_screen: bool,
_prompt_title: Option<TString<'static>>,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = {
let action = action.unwrap_or("".into());
@ -946,6 +947,7 @@ impl FirmwareUI for UIBolt {
false,
false,
None,
false,
)
}

View File

@ -58,6 +58,7 @@ impl FirmwareUI for UICaesar {
reverse: bool,
_prompt_screen: bool,
_prompt_title: Option<TString<'static>>,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = {
let action = action.unwrap_or("".into());

View File

@ -30,6 +30,8 @@ const MENU_ITEM_INFO: usize = 1;
// Extra button at the top-right corner of the Action screen
#[derive(PartialEq)]
pub enum ConfirmActionExtra {
// Shows a menu button that simply returns INFO so it can be handled externally
ExternalMenu,
// Opens a menu which can (optionally) lead to an extra Info screen, or cancel the action
Menu(ConfirmActionMenuStrings),
// Shows a cancel button directly
@ -114,6 +116,7 @@ impl FlowController for ConfirmAction {
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
(Self::Action, FlowMsg::Info) => self.return_msg(FlowMsg::Info),
_ => self.do_nothing(),
}
}
@ -143,6 +146,7 @@ impl FlowController for ConfirmActionWithConfirmation {
fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) {
(Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
(Self::Action, FlowMsg::Info) => self.return_msg(FlowMsg::Info),
(Self::Confirmation, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(),
}
@ -227,6 +231,7 @@ pub fn new_confirm_action(
hold: bool,
prompt_screen: bool,
prompt_title: TString<'static>,
external_menu: bool,
) -> Result<SwipeFlow, error::Error> {
let paragraphs = {
let action = action.unwrap_or("".into());
@ -244,9 +249,19 @@ pub fn new_confirm_action(
paragraphs.into_paragraphs()
};
if external_menu && (prompt_screen || hold) {
return Err(Error::ValueError(
c"external_menu currently not supported in tandem with prompt_screen/hold",
));
}
new_confirm_action_simple(
paragraphs,
ConfirmActionExtra::Menu(ConfirmActionMenuStrings::new().with_verb_cancel(verb_cancel)),
if external_menu {
ConfirmActionExtra::ExternalMenu
} else {
ConfirmActionExtra::Menu(ConfirmActionMenuStrings::new().with_verb_cancel(verb_cancel))
},
ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)),
hold,
None,
@ -273,7 +288,7 @@ fn new_confirm_action_uni<T: Component + PaginateFull + MaybeTrace + 'static>(
.with_vertical_pages();
match extra {
ConfirmActionExtra::Menu { .. } => {
ConfirmActionExtra::Menu { .. } | ConfirmActionExtra::ExternalMenu => {
content = content.with_menu_button();
}
ConfirmActionExtra::Cancel => {
@ -327,8 +342,12 @@ fn create_flow(
let initial_page: &dyn FlowController = match (extra, prompt_screen.is_some()) {
(ConfirmActionExtra::Menu { .. }, false) => &ConfirmActionWithMenu::Action,
(ConfirmActionExtra::Menu { .. }, true) => &ConfirmActionWithMenuAndConfirmation::Action,
(ConfirmActionExtra::Cancel, false) => &ConfirmAction::Action,
(ConfirmActionExtra::Cancel, true) => &ConfirmActionWithConfirmation::Action,
(ConfirmActionExtra::Cancel | ConfirmActionExtra::ExternalMenu, false) => {
&ConfirmAction::Action
}
(ConfirmActionExtra::Cancel | ConfirmActionExtra::ExternalMenu, true) => {
&ConfirmActionWithConfirmation::Action
}
};
(

View File

@ -61,17 +61,19 @@ impl FirmwareUI for UIDelizia {
reverse: bool,
prompt_screen: bool,
prompt_title: Option<TString<'static>>,
external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
let flow = flow::confirm_action::new_confirm_action(
title,
action,
description,
subtitle,
verb_cancel,
if external_menu { None } else { verb_cancel },
reverse,
hold,
prompt_screen,
prompt_title.unwrap_or(TString::empty()),
external_menu,
)?;
Ok(flow)
}

View File

@ -57,6 +57,7 @@ impl FirmwareUI for UIEckhart {
reverse: bool,
_prompt_screen: bool,
_prompt_title: Option<TString<'static>>,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = {
let action = action.unwrap_or("".into());

View File

@ -32,6 +32,7 @@ pub trait FirmwareUI {
reverse: bool,
prompt_screen: bool,
prompt_title: Option<TString<'static>>,
external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error>;
fn confirm_address(

View File

@ -110,6 +110,7 @@ def confirm_action(
reverse: bool = False,
prompt_screen: bool = False,
prompt_title: str | None = None,
external_menu: bool = False,
) -> LayoutObj[UiResult]:
"""Confirm action."""

View File

@ -39,28 +39,48 @@ def confirm_action(
prompt_screen: bool = False,
prompt_title: str | None = None,
) -> Awaitable[None]:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
if description is not None and description_param is not None:
description = description.format(description_param)
return raise_if_not_confirmed(
trezorui_api.confirm_action(
title=title,
action=action,
description=description,
subtitle=subtitle,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
hold_danger=hold_danger,
reverse=reverse,
prompt_screen=prompt_screen,
prompt_title=prompt_title or title,
),
br_name,
br_code,
exc,
flow = trezorui_api.confirm_action(
title=title,
action=action,
description=description,
subtitle=subtitle,
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
hold_danger=hold_danger,
reverse=reverse,
prompt_screen=prompt_screen,
prompt_title=prompt_title or title,
external_menu=not (prompt_screen or hold),
)
if prompt_screen or hold:
# Note: multi-step confirm (prompt_screen/hold)
# can't work with external menus yet
return raise_if_not_confirmed(
flow,
br_name,
br_code,
exc,
)
else:
menu = Menu.root(
cancel=verb_cancel or TR.buttons__cancel,
)
return confirm_with_menu(
flow,
menu,
br_name,
br_code,
exc,
)
def confirm_single(
br_name: str,