What changed, and why it matters
This commit adds a new optional subtitle label to a vertical menu screen in the Trezor hardware wallet's user interface. It is a routine UI feature change with no apparent security relevance.
No security action required; review as normal UI code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch extends the VerticalMenuScreen Rust component in the Trezor firmware’s Eckhart UI layout to support an optional Label subtitle. It adds a with_subtitle() builder method, chooses between one-line and two-line subtitle heights based on text fitting, adjusts layout placement, and renders the subtitle. No cryptographic, input validation, memory safety, or privileged operation changes are present.
Changed components
core/embed/rust/src/ui/layout_eckhart/firmware/vertical_menu_screen.rsInspect captured patch +43 / −3
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/vertical_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/vertical_menu_screen.rs
index 067b78b5..09cd4eb8 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/vertical_menu_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/vertical_menu_screen.rs
@@ -3,11 +3,12 @@ use crate::{
ui::{
component::{
swipe_detect::{SwipeConfig, SwipeSettings},
- Component, Event, EventCtx, SwipeDetect,
+ text::{layout::LayoutFit, TextStyle},
+ Component, Event, EventCtx, Label, SwipeDetect, TextLayout,
},
event::SwipeEvent,
flow::Swipable,
- geometry::{Alignment2D, Direction, Rect},
+ geometry::{Alignment2D, Direction, Offset, Rect},
shape::{Renderer, ToifImage},
util::{animation_disabled, Pager},
},
@@ -17,6 +18,8 @@ use super::{constant::SCREEN, theme, Header, HeaderMsg, MenuItems, VerticalMenu,
pub struct VerticalMenuScreen<T> {
header: Header,
+ /// Optional subtitle label
+ subtitle: Option<Label<'static>>,
/// Scrollable vertical menu
menu: VerticalMenu<T>,
/// Base position of the menu sliding window to scroll around
@@ -39,9 +42,14 @@ pub enum VerticalMenuScreenMsg {
impl<T: MenuItems> VerticalMenuScreen<T> {
const TOUCH_SENSITIVITY_DIVIDER: i16 = 15;
+ const SUBTITLE_STYLE: TextStyle = theme::TEXT_MEDIUM_GREY;
+ const SUBTITLE_HEIGHT: i16 = 68;
+ const SUBTITLE_DOUBLE_HEIGHT: i16 = 100;
+
pub fn new(menu: VerticalMenu<T>) -> Self {
Self {
header: Header::new(TString::empty()),
+ subtitle: None,
menu,
offset_base: 0,
swipe: None,
@@ -56,6 +64,14 @@ impl<T: MenuItems> VerticalMenuScreen<T> {
self
}
+ pub fn with_subtitle(mut self, subtitle: TString<'static>) -> Self {
+ if !subtitle.is_empty() {
+ self.subtitle =
+ Some(Label::left_aligned(subtitle, Self::SUBTITLE_STYLE).vertically_centered());
+ }
+ self
+ }
+
/// Update swipe detection and buttons state based on menu size
pub fn initialize_screen(&mut self, ctx: &mut EventCtx) {
if animation_disabled() {
@@ -169,7 +185,30 @@ impl<T: MenuItems> Component for VerticalMenuScreen<T> {
debug_assert_eq!(bounds.height(), SCREEN.height());
debug_assert_eq!(bounds.width(), SCREEN.width());
- let (header_area, menu_area) = bounds.split_top(Header::HEADER_HEIGHT);
+ let (header_area, rest) = bounds.split_top(Header::HEADER_HEIGHT);
+
+ let menu_area = if let Some(subtitle) = &mut self.subtitle {
+ // Choose appropriate height for the subtitle
+ let subtitle_height = if let LayoutFit::OutOfBounds { .. } =
+ subtitle.text().map(|text| {
+ TextLayout::new(Self::SUBTITLE_STYLE)
+ .with_bounds(
+ Rect::from_size(Offset::new(bounds.width(), Self::SUBTITLE_HEIGHT))
+ .inset(theme::SIDE_INSETS),
+ )
+ .fit_text(text)
+ }) {
+ Self::SUBTITLE_DOUBLE_HEIGHT
+ } else {
+ Self::SUBTITLE_HEIGHT
+ };
+
+ let (subtitle_area, rest) = rest.split_top(subtitle_height);
+ subtitle.place(subtitle_area.inset(theme::SIDE_INSETS));
+ rest
+ } else {
+ rest
+ };
self.header.place(header_area);
self.menu.place(menu_area);
@@ -202,6 +241,7 @@ impl<T: MenuItems> Component for VerticalMenuScreen<T> {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.header.render(target);
+ self.subtitle.render(target);
self.menu.render(target);
self.render_overflow_arrow(target);
}
Why this scored 15/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.