refactor(eckhart): subtext marquee when too long
What changed, and why it matters
This commit is a routine user-interface cleanup for the Trezor hardware wallet. It removes a manual 'marquee' flag and instead makes any long subtext automatically scroll sideways. There is no indication this change fixes or introduces a security problem.
No security action required; treat as normal UI refactor.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the Eckhart layout’s Button component so that subtext rendering always creates a Marquee object, and the decision to scroll is made at render time based on whether the text fits. It deletes the subtext_is_marquee field and related constructors, renames several methods from ‘_marquee’ to ‘_subtext’, and updates the device menu screen to use a single_line flag instead of a marquee flag. The previous debug-only fatal_error! for over-long non-marquee subtext is removed. No security-relevant logic is changed.
Changed components
core/embed/rust/src/ui/layout_eckhart/component/button.rscore/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rsInspect captured patch +27 / −73
diff --git a/core/embed/rust/src/ui/layout_eckhart/component/button.rs b/core/embed/rust/src/ui/layout_eckhart/component/button.rs
index e9fc82da..a0354e2a 100644
--- a/core/embed/rust/src/ui/layout_eckhart/component/button.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/component/button.rs
@@ -65,20 +65,13 @@ impl Button {
ButtonContent::TextAndSubtext {
subtext,
subtext_style,
- subtext_is_marquee,
..
- } => {
- if subtext_is_marquee {
- Some(Marquee::new(
- subtext,
- subtext_style.text_font,
- subtext_style.text_color,
- subtext_style.background_color,
- ))
- } else {
- None
- }
- }
+ } => Some(Marquee::new(
+ subtext,
+ subtext_style.text_font,
+ subtext_style.text_color,
+ subtext_style.background_color,
+ )),
_ => None,
};
Self {
@@ -127,13 +120,13 @@ impl Button {
.with_radius(Self::MENU_ITEM_RADIUS)
}
- pub fn new_single_line_menu_item_with_subtext_marquee(
+ pub fn new_single_line_menu_item_with_subtext(
text: TString<'static>,
stylesheet: ButtonStyleSheet,
subtext: TString<'static>,
subtext_style: &'static TextStyle,
) -> Self {
- Self::with_single_line_text_and_subtext_marquee(text, subtext, subtext_style, None)
+ Self::with_single_line_text_and_subtext(text, subtext, subtext_style, None)
.with_text_align(Self::MENU_ITEM_ALIGNMENT)
.with_content_offset(Self::MENU_ITEM_CONTENT_OFFSET)
.styled(stylesheet)
@@ -166,7 +159,7 @@ impl Button {
)
};
- Self::with_text_and_subtext_marquee(
+ Self::with_text_and_subtext(
text,
subtext,
subtext_style,
@@ -200,27 +193,13 @@ impl Button {
))
}
- pub fn with_single_line_text_and_subtext_marquee(
- text: TString<'static>,
- subtext: TString<'static>,
- subtext_style: &'static TextStyle,
- icon: Option<(Icon, Color)>,
- ) -> Self {
- Self::new(ButtonContent::single_line_text_and_marquee_subtext(
- text,
- subtext,
- subtext_style,
- icon,
- ))
- }
-
- pub fn with_text_and_subtext_marquee(
+ pub fn with_single_line_text_and_subtext(
text: TString<'static>,
subtext: TString<'static>,
subtext_style: &'static TextStyle,
icon: Option<(Icon, Color)>,
) -> Self {
- Self::new(ButtonContent::text_and_marquee_subtext(
+ Self::new(ButtonContent::single_line_text_and_subtext(
text,
subtext,
subtext_style,
@@ -542,7 +521,6 @@ impl Button {
single_line,
subtext,
subtext_style,
- subtext_is_marquee,
icon,
} => {
let text_baseline_height = self.baseline_text_height();
@@ -583,13 +561,12 @@ impl Button {
subtext.map(|subtext| {
let subtext_fits =
subtext_style.text_font.text_width(subtext) <= available_width;
- #[cfg(feature = "ui_debug")]
- {
- if !subtext_fits && !subtext_is_marquee {
- fatal_error!(&uformat!(len: 128, "Subtext too long: '{}'", subtext));
- }
- }
- if subtext_fits || !subtext_is_marquee || self.subtext_marquee.is_none() {
+ if self.subtext_marquee.is_some() && !subtext_fits {
+ let Some(m) = &self.subtext_marquee else {
+ unreachable!();
+ };
+ m.render(target);
+ } else {
shape::Text::new(
render_origin(if single_line_text {
text_baseline_height / 2
@@ -607,8 +584,6 @@ impl Button {
.with_align(self.text_align)
.with_alpha(alpha)
.render(target);
- } else if let Some(m) = &self.subtext_marquee {
- m.render(target);
}
});
@@ -842,7 +817,6 @@ pub enum ButtonContent {
single_line: bool,
subtext: TString<'static>,
subtext_style: &'static TextStyle,
- subtext_is_marquee: bool,
icon: Option<(Icon, Color)>,
},
Icon(Icon),
@@ -876,12 +850,11 @@ impl ButtonContent {
single_line: false,
subtext,
subtext_style,
- subtext_is_marquee: false,
icon,
}
}
- pub const fn single_line_text_and_marquee_subtext(
+ pub const fn single_line_text_and_subtext(
text: TString<'static>,
subtext: TString<'static>,
subtext_style: &'static TextStyle,
@@ -892,23 +865,6 @@ impl ButtonContent {
single_line: true,
subtext,
subtext_style,
- subtext_is_marquee: true,
- icon,
- }
- }
-
- pub const fn text_and_marquee_subtext(
- text: TString<'static>,
- subtext: TString<'static>,
- subtext_style: &'static TextStyle,
- icon: Option<(Icon, Color)>,
- ) -> Self {
- Self::TextAndSubtext {
- text,
- single_line: false,
- subtext,
- subtext_style,
- subtext_is_marquee: true,
icon,
}
}
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
index ee6c14dc..8667a65b 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/device_menu_screen.rs
@@ -157,8 +157,8 @@ impl<const N: usize> VecExt for Vec<MenuItem, N> {
struct MenuItem {
text: TString<'static>,
+ single_line: bool,
subtext: Option<(TString<'static>, Option<&'static TextStyle>)>,
- subtext_marquee: bool,
stylesheet: &'static ButtonStyleSheet,
connection_status: Option<bool>,
action: Option<Action>,
@@ -172,8 +172,8 @@ impl MenuItem {
fn new(text: TString<'static>, action: Option<Action>) -> Self {
Self {
text,
+ single_line: false,
subtext: None,
- subtext_marquee: false,
stylesheet: MENU_ITEM_NORMAL,
action,
connection_status: None,
@@ -198,8 +198,8 @@ impl MenuItem {
self
}
- pub fn with_subtext_marquee(mut self) -> Self {
- self.subtext_marquee = true;
+ pub fn with_single_line(mut self) -> Self {
+ self.single_line = true;
self
}
@@ -451,9 +451,7 @@ impl DeviceMenuScreen {
MenuItem::go_to_subscreen(text, device).with_connection_status(connection_status);
if let Some(subtext) = subtext {
- item_device = item_device
- .with_subtext(Some((subtext, None)))
- .with_subtext_marquee();
+ item_device = item_device.with_subtext(Some((subtext, None)));
}
items.add(item_device);
@@ -632,8 +630,8 @@ impl DeviceMenuScreen {
if let Some(device_name) = device_name {
let item_device_name =
MenuItem::return_msg(TR::words__name.into(), DeviceMenuMsg::SetDeviceName)
- .with_subtext(Some((device_name, None)))
- .with_subtext_marquee();
+ .with_single_line()
+ .with_subtext(Some((device_name, None)));
items.add(item_device_name);
}
@@ -796,8 +794,8 @@ impl DeviceMenuScreen {
} else if let Some((subtext, subtext_style)) = item.subtext {
let subtext_style =
subtext_style.unwrap_or(&theme::TEXT_MENU_ITEM_SUBTITLE);
- let ctor = if item.subtext_marquee {
- Button::new_single_line_menu_item_with_subtext_marquee
+ let ctor = if item.single_line {
+ Button::new_single_line_menu_item_with_subtext
} else {
Button::new_menu_item_with_subtext
};
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.