From 2bc787a652db4e10e7b07ad737ecf3a021ee052d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ioan=20Biz=C4=83u?= Date: Thu, 14 Nov 2024 12:35:31 +0100 Subject: [PATCH] feat(core/ui): add cancel button to paginated blobs [no changelog] --- core/embed/rust/librust_qstr.h | 1 + .../ui/model_mercury/flow/confirm_action.rs | 321 +++++++++++------- .../rust/src/ui/model_mercury/flow/mod.rs | 2 +- .../rust/src/ui/model_mercury/flow/util.rs | 21 +- .../embed/rust/src/ui/model_mercury/layout.rs | 41 ++- core/embed/rust/src/ui/model_tr/layout.rs | 1 + core/embed/rust/src/ui/model_tt/layout.rs | 1 + core/mocks/generated/trezorui2.pyi | 3 + .../src/trezor/ui/layouts/mercury/__init__.py | 2 + 9 files changed, 251 insertions(+), 142 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index 449f802f27..4606af2843 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -174,6 +174,7 @@ static void _librust_qstrs(void) { MP_QSTR_buttons__turn_on; MP_QSTR_buttons__view_all_data; MP_QSTR_can_go_back; + MP_QSTR_cancel; MP_QSTR_cancel_arrow; MP_QSTR_cancel_cross; MP_QSTR_cancel_text; diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs index e68d99aebf..51c8dceee1 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs @@ -25,94 +25,20 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum ConfirmAction { - Intro, - Menu, - Confirm, -} +const MENU_ITEM_CANCEL: usize = 0; +const MENU_ITEM_INFO: usize = 1; -impl FlowController for ConfirmAction { - fn index(&'static self) -> usize { - *self as usize - } - - fn handle_swipe(&'static self, direction: Direction) -> Decision { - match (self, direction) { - (Self::Intro, Direction::Left) => Self::Menu.swipe(direction), - (Self::Menu, Direction::Right) => Self::Intro.swipe(direction), - (Self::Intro, Direction::Up) => Self::Confirm.swipe(direction), - (Self::Confirm, Direction::Down) => Self::Intro.swipe(direction), - (Self::Confirm, Direction::Left) => Self::Menu.swipe(direction), - _ => self.do_nothing(), - } - } - - fn handle_event(&'static self, msg: FlowMsg) -> Decision { - match (self, msg) { - (Self::Intro, FlowMsg::Info) => Self::Menu.goto(), - (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), - (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), - (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info), - (Self::Confirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), - (Self::Confirm, FlowMsg::Info) => Self::Menu.goto(), - _ => self.do_nothing(), - } - } -} - -/// ConfirmAction flow without a separate "Tap to confirm" or "Hold to confirm" -/// screen. Swiping up directly from the intro screen confirms action. -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum ConfirmActionSimple { - Intro, - Menu, -} - -impl FlowController for ConfirmActionSimple { - #[inline] - fn index(&'static self) -> usize { - *self as usize - } - - fn handle_swipe(&'static self, direction: Direction) -> Decision { - match (self, direction) { - (Self::Intro, Direction::Left) => Self::Menu.swipe(direction), - (Self::Menu, Direction::Right) => Self::Intro.swipe(direction), - (Self::Intro, Direction::Up) => self.return_msg(FlowMsg::Confirmed), - _ => self.do_nothing(), - } - } - - fn handle_event(&'static self, msg: FlowMsg) -> Decision { - match (self, msg) { - (Self::Intro, FlowMsg::Info) => Self::Menu.goto(), - (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), - (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), - (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info), - _ => self.do_nothing(), - } - } -} - -pub struct ConfirmActionMenu { - verb_cancel: Option>, - info: bool, - verb_info: Option>, -} - -impl ConfirmActionMenu { - pub fn new( +// Extra button at the top-right corner of the Action screen +#[derive(PartialEq)] +pub enum ConfirmActionExtra { + // Opens a menu which can (optionally) lead to an extra Info screen, or cancel the action + Menu { verb_cancel: Option>, - info: bool, + has_info: bool, verb_info: Option>, - ) -> Self { - Self { - verb_cancel, - info, - verb_info, - } - } + }, + // Shows a cancel button directly + Cancel, } pub struct ConfirmActionStrings { @@ -138,6 +64,106 @@ impl ConfirmActionStrings { } } +/// The simplest form of the ConfirmAction flow: +/// no menu, nor a separate "Tap to confirm" or "Hold to confirm". +#[derive(Copy, Clone, PartialEq, Eq)] +enum ConfirmAction { + Action, +} + +impl FlowController for ConfirmAction { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: Direction) -> Decision { + match (self, direction) { + (Self::Action, Direction::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), + } + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Action, FlowMsg::Cancelled) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), + } + } +} + +/// A ConfirmAction flow with a menu which can contain various items +/// as defined by ConfirmActionExtra::Menu. +#[derive(Copy, Clone, PartialEq, Eq)] +enum ConfirmActionWithMenu { + Action, + Menu, +} + +impl FlowController for ConfirmActionWithMenu { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: Direction) -> Decision { + match (self, direction) { + (Self::Action, Direction::Left) => Self::Menu.swipe(direction), + (Self::Menu, Direction::Right) => Self::Action.swipe(direction), + (Self::Action, Direction::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), + } + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Action, FlowMsg::Info) => Self::Menu.goto(), + (Self::Menu, FlowMsg::Cancelled) => Self::Action.swipe_right(), + (Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Choice(MENU_ITEM_INFO)) => self.return_msg(FlowMsg::Info), + _ => self.do_nothing(), + } + } +} + +// A ConfirmAction flow with a menu and +// a separate "Tap to confirm" or "Hold to confirm" screen. +#[derive(Copy, Clone, PartialEq, Eq)] +enum ConfirmActionWithMenuAndConfirmation { + Action, + Menu, + Confirmation, +} + +impl FlowController for ConfirmActionWithMenuAndConfirmation { + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: Direction) -> Decision { + match (self, direction) { + (Self::Action, Direction::Left) => Self::Menu.swipe(direction), + (Self::Menu, Direction::Right) => Self::Action.swipe(direction), + (Self::Action, Direction::Up) => Self::Confirmation.swipe(direction), + (Self::Confirmation, Direction::Down) => Self::Action.swipe(direction), + (Self::Confirmation, Direction::Left) => Self::Menu.swipe(direction), + _ => self.do_nothing(), + } + } + + fn handle_event(&'static self, msg: FlowMsg) -> Decision { + match (self, msg) { + (Self::Action, FlowMsg::Info) => Self::Menu.goto(), + (Self::Menu, FlowMsg::Cancelled) => Self::Action.swipe_right(), + (Self::Menu, FlowMsg::Choice(MENU_ITEM_CANCEL)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Choice(MENU_ITEM_INFO)) => self.return_msg(FlowMsg::Info), + (Self::Confirmation, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Confirmation, FlowMsg::Info) => Self::Menu.goto(), + _ => self.do_nothing(), + } + } +} + #[allow(clippy::too_many_arguments)] pub fn new_confirm_action( title: TString<'static>, @@ -168,7 +194,11 @@ pub fn new_confirm_action( new_confirm_action_simple( paragraphs, - ConfirmActionMenu::new(verb_cancel, false, None), + ConfirmActionExtra::Menu { + verb_cancel, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)), hold, None, @@ -179,21 +209,29 @@ pub fn new_confirm_action( #[inline(never)] fn new_confirm_action_uni( content: SwipeContent>, - menu: ConfirmActionMenu, + extra: ConfirmActionExtra, strings: ConfirmActionStrings, hold: bool, show_page_counter: bool, ) -> Result { let (prompt_screen, prompt_pages, flow, page) = - create_flow(strings.title, strings.prompt_screen, hold); + create_flow(strings.title, strings.prompt_screen, hold, &extra); - let mut content_intro = Frame::left_aligned(strings.title, content) + let mut content = Frame::left_aligned(strings.title, content) .with_swipe(Direction::Up, SwipeSettings::default()) .with_swipe(Direction::Left, SwipeSettings::default()) .with_vertical_pages() - .with_menu_button() .with_footer(TR::instructions__swipe_up.into(), None); + match extra { + ConfirmActionExtra::Menu { .. } => { + content = content.with_menu_button(); + } + ConfirmActionExtra::Cancel => { + content = content.with_cancel_button(); + } + } + if show_page_counter { fn footer_update_fn( content: &SwipeContent>, @@ -205,26 +243,25 @@ fn new_confirm_action_uni( footer.update_page_counter(ctx, current_page, page_count); } - content_intro = content_intro + content = content .with_footer_counter(TR::instructions__swipe_up.into()) .register_footer_update_fn(footer_update_fn::); } if let Some(subtitle) = strings.subtitle { - content_intro = content_intro.with_subtitle(subtitle); + content = content.with_subtitle(subtitle); } - let content_intro = content_intro + let content = content .map(move |msg| match msg { - FrameMsg::Button(_) => Some(FlowMsg::Info), + FrameMsg::Button(FlowMsg::Info) => Some(FlowMsg::Info), + FrameMsg::Button(FlowMsg::Cancelled) => Some(FlowMsg::Cancelled), _ => None, }) .with_pages(move |intro_pages| intro_pages + prompt_pages); - let flow = flow?.with_page(page, content_intro)?; - - let flow = create_menu(flow, menu, prompt_screen)?; - + let flow = flow?.with_page(page, content)?; + let flow = create_menu(flow, extra, prompt_screen)?; let flow = create_confirm(flow, strings.subtitle, hold, prompt_screen)?; Ok(flow) @@ -234,6 +271,7 @@ fn create_flow( title: TString<'static>, prompt_screen: Option>, hold: bool, + extra: &ConfirmActionExtra, ) -> ( Option>, usize, @@ -242,53 +280,69 @@ fn create_flow( ) { let prompt_screen = prompt_screen.or_else(|| hold.then_some(title)); let prompt_pages: usize = prompt_screen.is_some().into(); - - let (flow, page): (Result, &dyn FlowController) = if prompt_screen.is_some() { - (SwipeFlow::new(&ConfirmAction::Intro), &ConfirmAction::Intro) - } else { - ( - SwipeFlow::new(&ConfirmActionSimple::Intro), - &ConfirmActionSimple::Intro, - ) + let initial_page: &dyn FlowController = match (extra, prompt_screen.is_some()) { + (ConfirmActionExtra::Menu { .. }, false) => &ConfirmActionWithMenu::Action, + (ConfirmActionExtra::Menu { .. }, true) => &ConfirmActionWithMenuAndConfirmation::Action, + _ => &ConfirmAction::Action, }; - (prompt_screen, prompt_pages, flow, page) + ( + prompt_screen, + prompt_pages, + SwipeFlow::new(initial_page), + initial_page, + ) } fn create_menu( flow: SwipeFlow, - menu: ConfirmActionMenu, + extra: ConfirmActionExtra, prompt_screen: Option>, ) -> Result { - let mut menu_choices = VerticalMenu::empty().danger( - theme::ICON_CANCEL, - menu.verb_cancel.unwrap_or(TR::buttons__cancel.into()), - ); - - if menu.info { - menu_choices = menu_choices.item( - theme::ICON_CHEVRON_RIGHT, - menu.verb_info - .unwrap_or(TR::words__title_information.into()), + if let ConfirmActionExtra::Menu { + verb_cancel, + has_info, + verb_info, + } = extra + { + // NB: The Cancel menu item is always the first, + // because of the MENU_ITEM_CANCEL = 0. + // If we want the cancel item to be somewhere else, + // we would need to account for that and we could not use a constant. + let mut menu_choices = VerticalMenu::empty().danger( + theme::ICON_CANCEL, + verb_cancel.unwrap_or(TR::buttons__cancel.into()), ); - } - let content_menu = Frame::left_aligned("".into(), menu_choices) - .with_cancel_button() - .with_swipe(Direction::Right, SwipeSettings::immediate()); + if has_info { + // The Info menu item (if present) has to be the 2nd, + // because of MENU_ITEM_INFO = 1! + menu_choices = menu_choices.item( + theme::ICON_CHEVRON_RIGHT, + verb_info.unwrap_or(TR::words__title_information.into()), + ); + } - let content_menu = content_menu.map(move |msg| match msg { - FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)), - FrameMsg::Button(_) => Some(FlowMsg::Cancelled), - }); + let content_menu = Frame::left_aligned("".into(), menu_choices) + .with_cancel_button() + .with_swipe(Direction::Right, SwipeSettings::immediate()); - if prompt_screen.is_some() { - flow.with_page(&ConfirmAction::Menu, content_menu) + let content_menu = content_menu.map(move |msg| match msg { + FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)), + FrameMsg::Button(_) => Some(FlowMsg::Cancelled), + }); + + if prompt_screen.is_some() { + flow.with_page(&ConfirmActionWithMenuAndConfirmation::Menu, content_menu) + } else { + flow.with_page(&ConfirmActionWithMenu::Menu, content_menu) + } } else { - flow.with_page(&ConfirmActionSimple::Menu, content_menu) + Ok(flow) // no menu being added } } +// Create the extra confirmation screen (optional). fn create_confirm( flow: SwipeFlow, subtitle: Option>, @@ -324,7 +378,10 @@ fn create_confirm( _ => None, }); - flow.with_page(&ConfirmAction::Confirm, content_confirm) + flow.with_page( + &ConfirmActionWithMenuAndConfirmation::Confirmation, + content_confirm, + ) } else { Ok(flow) } @@ -333,7 +390,7 @@ fn create_confirm( #[inline(never)] pub fn new_confirm_action_simple( content: T, - menu: ConfirmActionMenu, + extra: ConfirmActionExtra, strings: ConfirmActionStrings, hold: bool, page_limit: Option, @@ -341,7 +398,7 @@ pub fn new_confirm_action_simple ) -> Result { new_confirm_action_uni( SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)), - menu, + extra, strings, hold, show_page_counter, diff --git a/core/embed/rust/src/ui/model_mercury/flow/mod.rs b/core/embed/rust/src/ui/model_mercury/flow/mod.rs index 9a646aec3d..94f31b5e41 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/mod.rs @@ -19,7 +19,7 @@ pub mod util; pub mod warning_hi_prio; pub use confirm_action::{ - new_confirm_action, new_confirm_action_simple, ConfirmActionMenu, ConfirmActionStrings, + new_confirm_action, new_confirm_action_simple, ConfirmActionExtra, ConfirmActionStrings, }; #[cfg(feature = "universal_fw")] pub use confirm_fido::new_confirm_fido; diff --git a/core/embed/rust/src/ui/model_mercury/flow/util.rs b/core/embed/rust/src/ui/model_mercury/flow/util.rs index f7ca0556a8..5907cf7b2f 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/util.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/util.rs @@ -3,7 +3,7 @@ use super::{ component::{Frame, FrameMsg}, theme, }, - ConfirmActionMenu, ConfirmActionStrings, + ConfirmActionExtra, ConfirmActionStrings, }; use crate::{ error::Error, @@ -52,6 +52,7 @@ pub struct ConfirmBlobParams { swipe_up: bool, swipe_down: bool, swipe_right: bool, + cancel: bool, } impl ConfirmBlobParams { @@ -84,6 +85,7 @@ impl ConfirmBlobParams { swipe_up: false, swipe_down: false, swipe_right: false, + cancel: false, } } @@ -152,6 +154,11 @@ impl ConfirmBlobParams { self } + pub const fn with_cancel(mut self, cancel: bool) -> Self { + self.cancel = cancel; + self + } + pub const fn with_footer( mut self, instruction: TString<'static>, @@ -262,9 +269,19 @@ impl ConfirmBlobParams { } .into_paragraphs(); + let confirm_extra = if self.cancel { + ConfirmActionExtra::Cancel + } else { + ConfirmActionExtra::Menu { + verb_cancel: self.verb_cancel, + has_info: self.info_button, + verb_info: self.verb_info, + } + }; + flow::new_confirm_action_simple( paragraphs, - ConfirmActionMenu::new(self.verb_cancel, self.info_button, self.verb_info), + confirm_extra, ConfirmActionStrings::new( self.title, self.subtitle, diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index c4b05570f0..10c9e46fe9 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -55,7 +55,7 @@ use crate::{ flow::{ new_confirm_action_simple, util::{ConfirmBlobParams, ShowInfoParams}, - ConfirmActionMenu, ConfirmActionStrings, + ConfirmActionExtra, ConfirmActionStrings, }, theme::ICON_BULLET_CHECKMARK, }, @@ -246,7 +246,11 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m new_confirm_action_simple( FormattedText::new(ops).vertically_centered(), - ConfirmActionMenu::new(None, false, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new(title, None, None, Some(title)), false, None, @@ -296,6 +300,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map .get(Qstr::MP_QSTR_page_limit) .unwrap_or_else(|_| Obj::const_none()) .try_into_option()?; + let cancel: bool = kwargs.get_or(Qstr::MP_QSTR_cancel, false)?; let (description, description_font) = if page_limit == Some(1) { ( @@ -318,6 +323,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map .with_chunkify(chunkify) .with_page_counter(page_counter) .with_page_limit(page_limit) + .with_cancel(cancel) .with_prompt(prompt_screen) .with_hold(hold) .into_flow() @@ -389,7 +395,11 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut flow::new_confirm_action_simple( paragraphs, - ConfirmActionMenu::new(None, false, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new(title, None, None, None), false, None, @@ -432,7 +442,11 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m new_confirm_action_simple( paragraphs.into_paragraphs(), - ConfirmActionMenu::new(None, false, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new(title, None, None, hold.then_some(title)), hold, None, @@ -462,7 +476,11 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m new_confirm_action_simple( paragraphs, - ConfirmActionMenu::new(None, false, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new( TR::homescreen__settings_title.into(), Some(TR::homescreen__settings_subtitle.into()), @@ -774,7 +792,11 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma new_confirm_action_simple( paragraphs.into_paragraphs(), - ConfirmActionMenu::new(None, true, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: true, + verb_info: None, + }, ConfirmActionStrings::new(title, None, None, Some(title)), true, None, @@ -1091,7 +1113,11 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut new_confirm_action_simple( paragraphs, - ConfirmActionMenu::new(None, false, None), + ConfirmActionExtra::Menu { + verb_cancel: None, + has_info: false, + verb_info: None, + }, ConfirmActionStrings::new( TR::coinjoin__title.into(), None, @@ -1587,6 +1613,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// page_counter: bool = False, /// prompt_screen: bool = False, /// page_limit: int | None = None, + /// cancel: bool = False, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index ca5f82e7ac..dbb0e51ef7 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -1712,6 +1712,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// page_counter: bool = False, /// prompt_screen: bool = False, /// page_limit: int | None = None, + /// cancel: bool = False, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index f71906512e..6a9e69dd9f 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -1806,6 +1806,7 @@ pub static mp_module_trezorui2: Module = obj_module! { /// page_counter: bool = False, /// prompt_screen: bool = False, /// page_limit: int | None = None, + /// cancel: bool = False, /// ) -> LayoutObj[UiResult]: /// """Confirm byte sequence data.""" Qstr::MP_QSTR_confirm_blob => obj_fn_kw!(0, new_confirm_blob).as_obj(), diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index 61df2e7fe1..fbcbd016c1 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -71,6 +71,7 @@ def confirm_blob( page_counter: bool = False, prompt_screen: bool = False, page_limit: int | None = None, + cancel: bool = False, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" @@ -644,6 +645,7 @@ def confirm_blob( page_counter: bool = False, prompt_screen: bool = False, page_limit: int | None = None, + cancel: bool = False, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" @@ -1216,6 +1218,7 @@ def confirm_blob( page_counter: bool = False, prompt_screen: bool = False, page_limit: int | None = None, + cancel: bool = False, ) -> LayoutObj[UiResult]: """Confirm byte sequence data.""" diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index c0cdd92754..2ddbdec518 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -480,6 +480,7 @@ def confirm_blob( info_layout = trezorui2.confirm_blob( title=title, data=data, + subtitle=description, description=None, verb=None, verb_cancel=verb_cancel, @@ -488,6 +489,7 @@ def confirm_blob( chunkify=chunkify, page_counter=True, prompt_screen=False, + cancel=True, ) return with_info(