1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-23 06:58: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) .get(Qstr::MP_QSTR_prompt_title)
.unwrap_or_else(|_| Obj::const_none()) .unwrap_or_else(|_| Obj::const_none())
.try_into_option()?; .try_into_option()?;
let external_menu: bool = kwargs.get_or(Qstr::MP_QSTR_external_menu, false)?;
let layout = ModelUI::confirm_action( let layout = ModelUI::confirm_action(
title, title,
@ -84,6 +85,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
reverse, reverse,
prompt_screen, prompt_screen,
prompt_title, prompt_title,
external_menu,
)?; )?;
Ok(LayoutObj::new_root(layout)?.into()) Ok(LayoutObj::new_root(layout)?.into())
}; };
@ -1351,6 +1353,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// reverse: bool = False, /// reverse: bool = False,
/// prompt_screen: bool = False, /// prompt_screen: bool = False,
/// prompt_title: str | None = None, /// prompt_title: str | None = None,
/// external_menu: bool = False,
/// ) -> LayoutObj[UiResult]: /// ) -> LayoutObj[UiResult]:
/// """Confirm action.""" /// """Confirm action."""
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(), 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, reverse: bool,
_prompt_screen: bool, _prompt_screen: bool,
_prompt_title: Option<TString<'static>>, _prompt_title: Option<TString<'static>>,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = { let paragraphs = {
let action = action.unwrap_or("".into()); let action = action.unwrap_or("".into());
@ -946,6 +947,7 @@ impl FirmwareUI for UIBolt {
false, false,
false, false,
None, None,
false,
) )
} }

View File

@ -58,6 +58,7 @@ impl FirmwareUI for UICaesar {
reverse: bool, reverse: bool,
_prompt_screen: bool, _prompt_screen: bool,
_prompt_title: Option<TString<'static>>, _prompt_title: Option<TString<'static>>,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> { ) -> Result<impl LayoutMaybeTrace, Error> {
let paragraphs = { let paragraphs = {
let action = action.unwrap_or("".into()); 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 // Extra button at the top-right corner of the Action screen
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum ConfirmActionExtra { 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 // Opens a menu which can (optionally) lead to an extra Info screen, or cancel the action
Menu(ConfirmActionMenuStrings), Menu(ConfirmActionMenuStrings),
// Shows a cancel button directly // Shows a cancel button directly
@ -114,6 +116,7 @@ impl FlowController for ConfirmAction {
fn handle_event(&'static self, msg: FlowMsg) -> Decision { fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) { match (self, msg) {
(Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled), (Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled),
(Self::Action, FlowMsg::Info) => self.return_msg(FlowMsg::Info),
_ => self.do_nothing(), _ => self.do_nothing(),
} }
} }
@ -143,6 +146,7 @@ impl FlowController for ConfirmActionWithConfirmation {
fn handle_event(&'static self, msg: FlowMsg) -> Decision { fn handle_event(&'static self, msg: FlowMsg) -> Decision {
match (self, msg) { match (self, msg) {
(Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled), (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::Confirmation, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed),
_ => self.do_nothing(), _ => self.do_nothing(),
} }
@ -227,6 +231,7 @@ pub fn new_confirm_action(
hold: bool, hold: bool,
prompt_screen: bool, prompt_screen: bool,
prompt_title: TString<'static>, prompt_title: TString<'static>,
external_menu: bool,
) -> Result<SwipeFlow, error::Error> { ) -> Result<SwipeFlow, error::Error> {
let paragraphs = { let paragraphs = {
let action = action.unwrap_or("".into()); let action = action.unwrap_or("".into());
@ -244,9 +249,19 @@ pub fn new_confirm_action(
paragraphs.into_paragraphs() 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( new_confirm_action_simple(
paragraphs, 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)), ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)),
hold, hold,
None, None,
@ -273,7 +288,7 @@ fn new_confirm_action_uni<T: Component + PaginateFull + MaybeTrace + 'static>(
.with_vertical_pages(); .with_vertical_pages();
match extra { match extra {
ConfirmActionExtra::Menu { .. } => { ConfirmActionExtra::Menu { .. } | ConfirmActionExtra::ExternalMenu => {
content = content.with_menu_button(); content = content.with_menu_button();
} }
ConfirmActionExtra::Cancel => { ConfirmActionExtra::Cancel => {
@ -327,8 +342,12 @@ fn create_flow(
let initial_page: &dyn FlowController = match (extra, prompt_screen.is_some()) { let initial_page: &dyn FlowController = match (extra, prompt_screen.is_some()) {
(ConfirmActionExtra::Menu { .. }, false) => &ConfirmActionWithMenu::Action, (ConfirmActionExtra::Menu { .. }, false) => &ConfirmActionWithMenu::Action,
(ConfirmActionExtra::Menu { .. }, true) => &ConfirmActionWithMenuAndConfirmation::Action, (ConfirmActionExtra::Menu { .. }, true) => &ConfirmActionWithMenuAndConfirmation::Action,
(ConfirmActionExtra::Cancel, false) => &ConfirmAction::Action, (ConfirmActionExtra::Cancel | ConfirmActionExtra::ExternalMenu, false) => {
(ConfirmActionExtra::Cancel, true) => &ConfirmActionWithConfirmation::Action, &ConfirmAction::Action
}
(ConfirmActionExtra::Cancel | ConfirmActionExtra::ExternalMenu, true) => {
&ConfirmActionWithConfirmation::Action
}
}; };
( (

View File

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

View File

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

View File

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

View File

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

View File

@ -39,28 +39,48 @@ def confirm_action(
prompt_screen: bool = False, prompt_screen: bool = False,
prompt_title: str | None = None, prompt_title: str | None = None,
) -> Awaitable[None]: ) -> Awaitable[None]:
from trezor.ui.layouts.menu import Menu, confirm_with_menu
if description is not None and description_param is not None: if description is not None and description_param is not None:
description = description.format(description_param) description = description.format(description_param)
return raise_if_not_confirmed( flow = trezorui_api.confirm_action(
trezorui_api.confirm_action( title=title,
title=title, action=action,
action=action, description=description,
description=description, subtitle=subtitle,
subtitle=subtitle, verb=verb,
verb=verb, verb_cancel=verb_cancel,
verb_cancel=verb_cancel, hold=hold,
hold=hold, hold_danger=hold_danger,
hold_danger=hold_danger, reverse=reverse,
reverse=reverse, prompt_screen=prompt_screen,
prompt_screen=prompt_screen, prompt_title=prompt_title or title,
prompt_title=prompt_title or title, external_menu=not (prompt_screen or hold),
),
br_name,
br_code,
exc,
) )
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( def confirm_single(
br_name: str, br_name: str,