refactor(core): support `ICON_DOWN` for `confirm_with_info()` in Caesar
What changed, and why it matters
This commit is a user-interface cleanup in the Trezor firmware's 'Caesar' layout. It replaces several hard-coded 'i' text labels with a shared 'info icon' helper and adds support for a down-arrow icon. There is no change to cryptographic operations, memory safety, authentication, or data handling. It appears to be a non-security refactor.
No security action required. Treat as routine UI refactor during normal review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff refactors button creation in core/embed/rust/src/ui/layout_caesar/component/button.rs and consumers. It introduces ButtonDetails::info_icon() and ButtonDetails::down_arrow_icon(), adds ‘V’ and ‘i’ to from_text_possible_icon(), and replaces inline ButtonDetails::text(‘i’…) constructions with the new helper. It also threads verb_info through ShowMore and updates Python callers in core/src/trezor/ui/layouts/caesar/init.py to pass INFO_ICON (‘i’) or DOWN_ARROW (‘V’) strings. No unsafe blocks, no boundary/permission changes, and no changelog entry (‘[no changelog]’).
Changed components
core/embed/rust/src/ui/layout_caesar/component/button.rscore/embed/rust/src/ui/layout_caesar/component/mod.rscore/embed/rust/src/ui/layout_caesar/component/page.rscore/embed/rust/src/ui/layout_caesar/component/show_more.rscore/embed/rust/src/ui/layout_caesar/ui_firmware.rscore/src/trezor/ui/layouts/caesar/__init__.pyInspect captured patch +56 / −35
diff --git a/core/embed/rust/src/ui/layout_caesar/component/button.rs b/core/embed/rust/src/ui/layout_caesar/component/button.rs
index 72b71a11..6924c6c4 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/button.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/button.rs
@@ -397,6 +397,8 @@ impl ButtonDetails {
"" => Self::cancel_icon(),
"<" => Self::left_arrow_icon(),
"^" => Self::up_arrow_icon(),
+ "V" => Self::down_arrow_icon(),
+ "i" => Self::info_icon(),
_ => Self::text(text),
})
}
@@ -411,6 +413,13 @@ impl ButtonDetails {
Self::icon(theme::ICON_CANCEL).with_offset(Offset::new(3, -3))
}
+ /// Info icon with an outline.
+ pub fn info_icon() -> Self {
+ Self::text("i".into())
+ .with_fixed_width(theme::BUTTON_ICON_WIDTH)
+ .with_font(fonts::FONT_NORMAL)
+ }
+
/// Left arrow to signal going back. No outline.
pub fn left_arrow_icon() -> Self {
Self::icon(theme::ICON_ARROW_LEFT).with_offset(Offset::new(4, -3))
@@ -427,6 +436,12 @@ impl ButtonDetails {
Self::icon(theme::ICON_ARROW_UP).with_offset(Offset::new(3, -4))
}
+ /// Down arrow to signal paginating forward. No outline. Offsetted little
+ /// right to not be on the boundary.
+ pub fn down_arrow_icon() -> Self {
+ Self::icon(theme::ICON_ARROW_DOWN).with_offset(Offset::new(-3, -4))
+ }
+
/// Down arrow to signal paginating forward. Takes half the screen's width
pub fn down_arrow_icon_wide() -> Self {
Self::icon(theme::ICON_ARROW_DOWN)
@@ -558,24 +573,38 @@ impl ButtonLayout {
Self::new(
Some(ButtonDetails::from_text_possible_icon(left)),
Some(ButtonDetails::armed_text(middle)),
- Some(
- ButtonDetails::text("i".into())
- .with_fixed_width(theme::BUTTON_ICON_WIDTH)
- .with_font(fonts::FONT_NORMAL),
- ),
+ Some(ButtonDetails::info_icon()),
)
}
- /// Left cancel, armed text and right info icon/text.
+ /// Left text, armed text and right info text.
+ pub fn text_armed_text(
+ left: TString<'static>,
+ middle: TString<'static>,
+ right: TString<'static>,
+ ) -> Self {
+ Self::new(
+ Some(ButtonDetails::from_text_possible_icon(left)),
+ Some(ButtonDetails::armed_text(middle)),
+ Some(ButtonDetails::from_text_possible_icon(right)),
+ )
+ }
+
+ /// Left cancel, armed text and right info icon.
pub fn cancel_armed_info(middle: TString<'static>) -> Self {
Self::new(
Some(ButtonDetails::cancel_icon()),
Some(ButtonDetails::armed_text(middle)),
- Some(
- ButtonDetails::text("i".into())
- .with_fixed_width(theme::BUTTON_ICON_WIDTH)
- .with_font(fonts::FONT_NORMAL),
- ),
+ Some(ButtonDetails::info_icon()),
+ )
+ }
+
+ /// Left cancel, armed text and right info icon/text.
+ pub fn cancel_armed_text(middle: TString<'static>, right: TString<'static>) -> Self {
+ Self::new(
+ Some(ButtonDetails::cancel_icon()),
+ Some(ButtonDetails::armed_text(middle)),
+ Some(ButtonDetails::from_text_possible_icon(right)),
)
}
@@ -692,11 +721,7 @@ impl ButtonLayout {
Self::new(
Some(ButtonDetails::up_arrow_icon()),
Some(ButtonDetails::armed_text(text)),
- Some(
- ButtonDetails::text("i".into())
- .with_fixed_width(theme::BUTTON_ICON_WIDTH)
- .with_font(fonts::FONT_NORMAL),
- ),
+ Some(ButtonDetails::info_icon()),
)
}
diff --git a/core/embed/rust/src/ui/layout_caesar/component/mod.rs b/core/embed/rust/src/ui/layout_caesar/component/mod.rs
index d31ba1c1..b4b3b9e8 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/mod.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/mod.rs
@@ -8,7 +8,7 @@ mod loader;
mod result;
mod welcome_screen;
-use super::{common_messages, constant, fonts, theme};
+use super::{common_messages, constant, theme};
pub use button::{
Button, ButtonAction, ButtonActions, ButtonContent, ButtonDetails, ButtonLayout, ButtonPos,
ButtonStyle, ButtonStyleSheet,
diff --git a/core/embed/rust/src/ui/layout_caesar/component/page.rs b/core/embed/rust/src/ui/layout_caesar/component/page.rs
index 820cc59c..643731c9 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/page.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/page.rs
@@ -10,8 +10,7 @@ use crate::{
};
use super::{
- constant, fonts, theme, ButtonController, ButtonControllerMsg, ButtonDetails, ButtonLayout,
- ButtonPos,
+ constant, theme, ButtonController, ButtonControllerMsg, ButtonDetails, ButtonLayout, ButtonPos,
};
pub struct ButtonPage<T>
@@ -114,11 +113,7 @@ where
(false, false) => (None, self.confirm_btn_details.clone()),
(false, true) => (
self.confirm_btn_details.clone().map(|b| b.with_arms()),
- Some(
- ButtonDetails::text("i".into())
- .with_fixed_width(theme::BUTTON_ICON_WIDTH)
- .with_font(fonts::FONT_NORMAL),
- ),
+ Some(ButtonDetails::info_icon()),
),
};
ButtonLayout::new(btn_left, btn_middle, btn_right)
diff --git a/core/embed/rust/src/ui/layout_caesar/component/show_more.rs b/core/embed/rust/src/ui/layout_caesar/component/show_more.rs
index b8b4bb68..80895469 100644
--- a/core/embed/rust/src/ui/layout_caesar/component/show_more.rs
+++ b/core/embed/rust/src/ui/layout_caesar/component/show_more.rs
@@ -30,11 +30,12 @@ where
content: T,
cancel_button: Option<TString<'static>>,
button: TString<'static>,
+ verb_info: TString<'static>,
) -> Self {
let btn_layout = if let Some(cancel_text) = cancel_button {
- ButtonLayout::text_armed_info(cancel_text, button)
+ ButtonLayout::text_armed_text(cancel_text, button, verb_info)
} else {
- ButtonLayout::cancel_armed_info(button)
+ ButtonLayout::cancel_armed_text(button, verb_info)
};
Self {
content: Child::new(content),
diff --git a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
index 12c1e9d5..41489d0f 100644
--- a/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
+++ b/core/embed/rust/src/ui/layout_caesar/ui_firmware.rs
@@ -515,11 +515,7 @@ impl FirmwareUI for UICaesar {
// if there are no info pages, the right button is not needed
// if verb_cancel is "^", the left button is an arrow pointing up
let left_btn = Some(ButtonDetails::from_text_possible_icon(verb_cancel));
- let right_btn = (has_pages_after || external_menu).then(|| {
- ButtonDetails::text("i".into())
- .with_fixed_width(theme::BUTTON_ICON_WIDTH)
- .with_font(fonts::FONT_NORMAL)
- });
+ let right_btn = (has_pages_after || external_menu).then(ButtonDetails::info_icon);
let middle_btn = Some(ButtonDetails::armed_text(TR::buttons__confirm.into()));
(
@@ -627,7 +623,7 @@ impl FirmwareUI for UICaesar {
_subtitle: Option<TString<'static>>,
items: Obj,
verb: TString<'static>,
- _verb_info: TString<'static>,
+ verb_info: TString<'static>,
verb_cancel: Option<TString<'static>>,
external_menu: bool,
) -> Result<impl LayoutMaybeTrace, Error> {
@@ -654,6 +650,7 @@ impl FirmwareUI for UICaesar {
paragraphs.into_paragraphs(),
verb_cancel,
verb,
+ verb_info,
)
.with_menu(external_menu),
));
diff --git a/core/src/trezor/ui/layouts/caesar/__init__.py b/core/src/trezor/ui/layouts/caesar/__init__.py
index 606daacf..6a6b0a47 100644
--- a/core/src/trezor/ui/layouts/caesar/__init__.py
+++ b/core/src/trezor/ui/layouts/caesar/__init__.py
@@ -23,6 +23,7 @@ CANCELLED = trezorui_api.CANCELLED
INFO = trezorui_api.INFO
DOWN_ARROW = "V"
+INFO_ICON = "i"
BR_CODE_OTHER = ButtonRequestType.Other # global_import_cache
@@ -605,7 +606,7 @@ async def confirm_payment_request(
title=title,
items=[(TR.words__provider, True), (recipient_name, False)],
verb=TR.buttons__continue,
- verb_info=TR.buttons__info,
+ verb_info=INFO_ICON,
external_menu=True,
)
@@ -721,13 +722,15 @@ async def should_show_more(
Raises ActionCancelled if the user cancels.
"""
+ if button_text != DOWN_ARROW:
+ button_text = INFO_ICON
result = await interact(
trezorui_api.confirm_with_info(
title=title,
items=para,
verb=confirm or TR.buttons__confirm,
verb_cancel=verb_cancel,
- verb_info=button_text or TR.buttons__show_all, # unused on caesar
+ verb_info=button_text, # use info icon by default
),
br_name,
br_code,
@@ -967,7 +970,7 @@ def confirm_value(
title=title,
items=((value, False),),
verb=verb or TR.buttons__confirm,
- verb_info=TR.buttons__info,
+ verb_info=INFO_ICON,
external_menu=True,
)
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.