refactor(core/delizia): utilize MoreInfoScreen
What changed, and why it matters
This commit is a routine internal code cleanup (refactor) for the Trezor firmware's user interface. It replaces a custom-built information screen with a shared 'MoreInfoScreen' component and simplifies a settings structure. There is no indication in the commit that it fixes a security bug or changes security behavior.
No security action required; treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors the Delizia UI layout: ShowInfoParams is reduced to only title/items, and its into_layout() now delegates to the reusable MoreInfoScreen component. The confirm_summary flow is updated to build its main screen directly from a Frame with paragraphs instead of using ShowInfoParams. MoreInfoScreen gains a Swipable implementation under the micropython feature. No functional security changes are evident.
Changed components
core/embed/rust/src/ui/layout_delizia/component/more_info_screen.rscore/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rscore/embed/rust/src/ui/layout_delizia/flow/util.rscore/embed/rust/src/ui/layout_delizia/ui_firmware.rsInspect captured patch +64 / −128
### core/embed/rust/src/ui/layout_delizia/component/more_info_screen.rs
@@ -1,6 +1,8 @@
use super::theme::{self, TITLE_HEIGHT};
use super::{ActionBar, ActionBarMsg, Header};
use crate::strutil::TString;
+#[cfg(feature = "micropython")]
+use crate::ui::component::swipe_detect::SwipeConfig;
use crate::ui::component::text::paragraphs::{ParagraphSource, Paragraphs};
use crate::ui::component::{Component, Event, EventCtx, FlowMsg, Paginate};
use crate::ui::geometry::{Insets, Rect};
@@ -129,6 +131,22 @@ where
}
}
+#[cfg(feature = "micropython")]
+impl<T> crate::ui::flow::Swipable for MoreInfoScreen<T>
+where
+ T: ParagraphSource<'static>,
+{
+ fn get_swipe_config(&self) -> SwipeConfig {
+ // No swipe navigation: the content is paginated via the action bar
+ // buttons and the screen is dismissed via the header close button.
+ SwipeConfig::default()
+ }
+
+ fn get_pager(&self) -> Pager {
+ self.pager()
+ }
+}
+
#[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for MoreInfoScreen<T>
where
### core/embed/rust/src/ui/layout_delizia/flow/confirm_summary.rs
@@ -9,9 +9,12 @@ use crate::micropython::Error;
use crate::strutil::TString;
use crate::translations::TR;
use crate::ui::component::swipe_detect::SwipeSettings;
+use crate::ui::component::text::paragraphs::{
+ Paragraph, ParagraphSource, ParagraphVecLong, VecExt,
+};
use crate::ui::component::ComponentExt;
use crate::ui::flow::base::{Decision, DecisionBuilder as _};
-use crate::ui::flow::{FlowController, FlowMsg, SwipeFlow};
+use crate::ui::flow::{FlowController, FlowMsg, SwipeFlow, SwipePage};
use crate::ui::geometry::Direction;
const MENU_ITEM_CANCEL: usize = 0;
@@ -58,8 +61,13 @@ impl FlowController for ConfirmSummary {
}
}
+#[allow(clippy::too_many_arguments)]
pub fn new_confirm_summary(
- summary_params: ShowInfoParams,
+ title: TString<'static>,
+ amount: Option<TString<'static>>,
+ amount_label: Option<TString<'static>>,
+ fee: TString<'static>,
+ fee_label: TString<'static>,
account_params: Option<ShowInfoParams>,
account_title: Option<TString<'static>>,
extra_params: Option<ShowInfoParams>,
@@ -68,12 +76,31 @@ pub fn new_confirm_summary(
can_go_back: bool,
) -> Result<SwipeFlow, Error> {
// Summary
- let mut content_summary = summary_params.with_flow_menu(true);
+ let mut summary_paragraphs = ParagraphVecLong::new();
+ if let (Some(amount_label), Some(amount)) = (amount_label, amount) {
+ summary_paragraphs.add(Paragraph::new(&theme::TEXT_SUB_GREY, amount_label).no_break());
+ summary_paragraphs.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, amount));
+ summary_paragraphs.add(Paragraph::new::<TString<'static>>(
+ &theme::TEXT_SUB_GREY,
+ " ".into(),
+ ));
+ }
+ summary_paragraphs.add(Paragraph::new(&theme::TEXT_SUB_GREY, fee_label).no_break());
+ summary_paragraphs.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, fee));
+
+ let mut summary_frame = Frame::with_header(
+ Header::left_aligned(title).with_menu_button(),
+ SwipeContent::new(SwipePage::vertical(summary_paragraphs.into_paragraphs())),
+ )
+ .with_footer(TR::instructions__tap_to_continue.into(), None)
+ .with_swipe(Direction::Up, SwipeSettings::Default)
+ .with_flow_menu();
if can_go_back {
- content_summary = content_summary.with_swipe_down();
+ summary_frame = summary_frame.with_swipe(Direction::Down, SwipeSettings::Default);
}
- let content_summary = content_summary
- .into_layout()?
+ let content_summary = summary_frame
+ .with_vertical_pages()
+ .map_to_button_msg()
// Summary(1) + Hold(1)
.with_pages(|summary_pages| summary_pages + 1);
### core/embed/rust/src/ui/layout_delizia/flow/util.rs
@@ -1,7 +1,7 @@
use heapless::Vec;
use super::super::component::{
- Frame, Header, PromptMsg, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
+ Frame, Header, MoreInfoScreen, PromptMsg, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
};
use super::super::{flow, theme};
use super::{
@@ -289,30 +289,14 @@ impl ConfirmValue {
pub struct ShowInfoParams {
title: TString<'static>,
- subtitle: Option<TString<'static>>,
- menu_button: bool,
- cancel_button: bool,
- footer_instruction: Option<TString<'static>>,
- footer_description: Option<TString<'static>>,
- swipe_up: bool,
- swipe_down: bool,
items: Vec<(TString<'static>, TString<'static>), 4>,
- flow_menu: bool,
}
impl ShowInfoParams {
pub const fn new(title: TString<'static>) -> Self {
Self {
title,
- subtitle: None,
- menu_button: false,
- cancel_button: false,
- footer_instruction: None,
- footer_description: None,
- swipe_up: false,
- swipe_down: false,
items: Vec::new(),
- flow_menu: false,
}
}
@@ -324,62 +308,6 @@ impl ShowInfoParams {
}
}
- pub fn is_empty(&self) -> bool {
- self.items.is_empty()
- }
-
- #[inline(never)]
- pub const fn with_subtitle(mut self, subtitle: Option<TString<'static>>) -> Self {
- self.subtitle = subtitle;
- self
- }
-
- #[inline(never)]
- pub const fn with_menu_button(mut self) -> Self {
- self.menu_button = true;
- self
- }
-
- #[inline(never)]
- pub const fn with_cancel_button(mut self) -> Self {
- self.cancel_button = true;
- self
- }
-
- #[inline(never)]
- pub const fn with_footer(
- mut self,
- instruction: TString<'static>,
- description: Option<TString<'static>>,
- ) -> Self {
- self.footer_instruction = Some(instruction);
- self.footer_description = description;
- self
- }
-
- pub const fn with_swipeup_footer(self, description: Option<TString<'static>>) -> Self {
- self.with_footer(
- TString::from_translation(TR::instructions__tap_to_continue),
- description,
- )
- .with_swipe_up()
- }
-
- pub const fn with_swipe_up(mut self) -> Self {
- self.swipe_up = true;
- self
- }
-
- pub const fn with_swipe_down(mut self) -> Self {
- self.swipe_down = true;
- self
- }
-
- pub const fn with_flow_menu(mut self, flow_menu: bool) -> Self {
- self.flow_menu = flow_menu;
- self
- }
-
#[inline(never)]
pub fn into_layout(
self,
@@ -399,41 +327,10 @@ impl ShowInfoParams {
paragraphs.add(Paragraph::new(&theme::TEXT_MONO_GREY_LIGHT, item.1));
}
- let mut header = Header::left_aligned(self.title);
- if let Some(subtitle) = self.subtitle {
- header = header.with_subtitle(subtitle);
- }
- if self.cancel_button {
- header = header.with_cancel_button()
- } else if self.menu_button {
- header = header.with_menu_button()
- }
-
- let mut frame = Frame::with_header(
- header,
- SwipeContent::new(SwipePage::vertical(paragraphs.into_paragraphs())),
- );
- if self.cancel_button {
- frame = frame.with_swipe(Direction::Right, SwipeSettings::Immediate);
- }
- if let Some(instruction) = self.footer_instruction {
- frame = frame.with_footer(instruction, self.footer_description);
- }
- if self.flow_menu {
- frame = frame.with_flow_menu();
- }
-
- if self.swipe_up {
- frame = frame.with_swipe(Direction::Up, SwipeSettings::Default);
- }
-
- if self.swipe_down {
- frame = frame.with_swipe(Direction::Down, SwipeSettings::Default);
- }
-
- frame = frame.with_vertical_pages();
-
- Ok(frame.map_to_button_msg())
+ Ok(MoreInfoScreen::new(
+ self.title,
+ paragraphs.into_paragraphs(),
+ ))
}
}
### core/embed/rust/src/ui/layout_delizia/ui_firmware.rs
@@ -397,20 +397,10 @@ impl FirmwareUI for UIDelizia {
back_button: bool,
_external_menu: bool, // TODO: will eventually replace the internal menu
) -> Result<impl LayoutMaybeTrace, Error> {
- let mut summary_params = ShowInfoParams::new(title.unwrap_or(TString::empty()))
- .with_menu_button()
- .with_swipeup_footer(None);
- if let Some(amount) = amount {
- if let Some(amount_label) = amount_label {
- summary_params = unwrap!(summary_params.add(amount_label, amount));
- }
- }
- summary_params = unwrap!(summary_params.add(fee_label, fee));
-
// collect available info
let account_params = if let Some(items) = account_items {
let account_title = account_title.unwrap_or(TR::send__send_from.into());
- let mut account_params = ShowInfoParams::new(account_title).with_cancel_button();
+ let mut account_params = ShowInfoParams::new(account_title);
for pair in IterBuf::new().try_iterate(items)? {
let [key, value, _is_data]: [Obj; 3] = util::iter_into_array(pair)?;
account_params = unwrap!(account_params.add(key.try_into()?, value.try_into()?));
@@ -421,7 +411,7 @@ impl FirmwareUI for UIDelizia {
};
let extra_params = if let Some(items) = extra_items {
let extra_title = extra_title.unwrap_or(TR::buttons__more_info.into());
- let mut extra_params = ShowInfoParams::new(extra_title).with_cancel_button();
+ let mut extra_params = ShowInfoParams::new(extra_title);
for pair in IterBuf::new().try_iterate(items)? {
let [label, value, _is_data]: [Obj; 3] = util::iter_into_array(pair)?;
extra_params = unwrap!(extra_params.add(label.try_into()?, value.try_into()?));
@@ -432,7 +422,11 @@ impl FirmwareUI for UIDelizia {
};
let flow = flow::new_confirm_summary(
- summary_params,
+ title.unwrap_or(TString::empty()),
+ amount,
+ amount_label,
+ fee,
+ fee_label,
account_params,
account_title,
extra_params,
@@ -1115,7 +1109,7 @@ impl FirmwareUI for UIDelizia {
let layout = confirm.into_layout()?;
flow::util::single_page(layout.map(|_| Some(FlowMsg::Confirmed)))
} else {
- let mut params = ShowInfoParams::new(title).with_cancel_button();
+ let mut params = ShowInfoParams::new(title);
for (header, text, _is_data) in items {
params = unwrap!(params.add(header, text));
}Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.