feat(core/eckhart): use different text in Progress
What changed, and why it matters
This commit is a routine user-interface layout tweak for the Trezor hardware wallet's Eckhart design. It changes how text is positioned on progress screens (for example, 'Signing transaction...' and 'Please wait') and moves some shared layout constants into a common theme file. There is no security-relevant change here.
No security action required. Treat as normal UI refactoring code review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the Eckhart layout’s progress screen rendering. It replaces a two-line centered Paragraphs widget with separate left-aligned title and centered description Labels, adjusts their placement rectangles, and centralizes CONTENT_INSETS_NO_HEADER and PROGRESS_TEXT_ORIGIN constants in theme/mod.rs. The bootloader progress screen is updated to use the same shared PROGRESS_TEXT_ORIGIN. No cryptographic, input-validation, memory-safety, or authentication logic is modified.
Changed components
core/embed/rust/src/ui/layout_eckhart/firmware/progress_screen.rscore/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rscore/embed/rust/src/ui/layout_eckhart/firmware/text_screen.rscore/embed/rust/src/ui/layout_eckhart/theme/mod.rscore/embed/rust/src/ui/layout_eckhart/ui_bootloader.rsInspect captured patch +46 / −44
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs
index 08bf2943..e209dd66 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs
@@ -55,8 +55,6 @@ pub enum HomescreenMsg {
}
impl Homescreen {
- const HOMELABEL_INSETS: Insets = Insets::top(38);
-
pub fn new(
label: TString<'static>,
_lockable: bool,
@@ -176,7 +174,7 @@ impl Component for Homescreen {
} else {
rest
};
- let label_area = rest.inset(theme::SIDE_INSETS).inset(Self::HOMELABEL_INSETS);
+ let label_area = rest.inset(theme::CONTENT_INSETS_NO_HEADER);
self.label.place(label_area);
self.action_bar.place(bar_area);
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/progress_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/progress_screen.rs
index 22098aa5..e3304950 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/progress_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/progress_screen.rs
@@ -4,11 +4,8 @@ use crate::{
strutil::TString,
translations::TR,
ui::{
- component::{
- text::paragraphs::{Paragraph, ParagraphSource as _, ParagraphVecShort, Paragraphs},
- Component, Event, EventCtx, Label, Never,
- },
- geometry::{Alignment2D, LinearPlacement, Offset, Rect},
+ component::{text::TextStyle, Component, Event, EventCtx, Label, Never},
+ geometry::{Alignment2D, Offset, Rect},
shape::Renderer,
util::animation_disabled,
},
@@ -17,14 +14,17 @@ use crate::{
use super::super::{
constant::SCREEN,
cshape::{render_loader, render_loader_indeterminate, ScreenBorder},
- theme,
+ fonts, theme,
};
const LOADER_SPEED: u16 = 5;
pub struct ProgressScreen {
indeterminate: bool,
- text: Paragraphs<ParagraphVecShort<'static>>,
+ /// Title of the progress screen, e.g. "Signing transaction..."
+ title: Label<'static>,
+ /// Description of the current progress, e.g. "Please wait"
+ description: Label<'static>,
/// Current value of the progress bar.
value: u16,
border: ScreenBorder,
@@ -42,7 +42,8 @@ impl ProgressScreen {
) -> Self {
Self {
indeterminate,
- text: Self::create_paragraphs(title, description),
+ title: Label::left_aligned(title, theme::TEXT_NORMAL),
+ description: Label::centered(description, theme::TEXT_SMALL_GREY).vertically_centered(),
value: 0,
border: ScreenBorder::new(if danger {
theme::ORANGE
@@ -61,7 +62,8 @@ impl ProgressScreen {
) -> Self {
Self {
indeterminate,
- text: Self::create_paragraphs(title, description),
+ title: Label::left_aligned(title, theme::TEXT_NORMAL),
+ description: Label::centered(description, theme::TEXT_SMALL_GREY).vertically_centered(),
value: 0,
border: ScreenBorder::new(theme::GREEN_LIME),
coinjoin_progress: true,
@@ -71,18 +73,6 @@ impl ProgressScreen {
),
}
}
-
- fn create_paragraphs(
- title: TString<'static>,
- description: TString<'static>,
- ) -> Paragraphs<ParagraphVecShort<'static>> {
- ParagraphVecShort::from_iter([
- Paragraph::new(&theme::firmware::TEXT_MEDIUM_GREY, title).centered(),
- Paragraph::new(&theme::firmware::TEXT_MEDIUM_GREY, description).centered(),
- ])
- .into_paragraphs()
- .with_placement(LinearPlacement::vertical().align_at_center())
- }
}
impl Component for ProgressScreen {
@@ -93,16 +83,26 @@ impl Component for ProgressScreen {
debug_assert_eq!(bounds.width(), SCREEN.width());
let bounds = bounds.inset(theme::SIDE_INSETS);
- let max_text_area = 3 * theme::TEXT_REGULAR.text_font.text_max_height();
+ let main_text_area = Rect::from_top_left_and_size(
+ theme::PROGRESS_TEXT_ORIGIN,
+ Offset::new(
+ SCREEN.width() - 2 * theme::PADDING,
+ 4 * theme::TEXT_NORMAL.text_font.text_max_height(),
+ ),
+ );
let middle_text_area = Rect::snap(
- SCREEN.center(),
- Offset::new(bounds.width(), max_text_area),
- Alignment2D::CENTER,
+ main_text_area.bottom_center(),
+ Offset::new(
+ bounds.width(),
+ 3 * theme::TEXT_REGULAR.text_font.text_max_height(),
+ ),
+ Alignment2D::TOP_CENTER,
);
let action_bar_area = bounds.split_bottom(theme::ACTION_BAR_HEIGHT).1;
self.coinjoin_do_not_disconnect.place(middle_text_area);
- self.text.place(action_bar_area);
+ self.title.place(main_text_area);
+ self.description.place(action_bar_area);
bounds
}
@@ -126,8 +126,8 @@ impl Component for ProgressScreen {
if !animation_disabled() {
ctx.request_paint();
}
- if self.text.inner()[1].content() != &new_description {
- self.text.mutate(|p| p[1].update(new_description));
+ if self.description.text() != &new_description {
+ self.description.set_text(new_description);
ctx.request_paint();
}
}
@@ -144,10 +144,11 @@ impl Component for ProgressScreen {
} else {
render_loader(progress_val, &self.border, target);
}
+ self.title.render(target);
+ self.description.render(target);
if self.coinjoin_progress {
self.coinjoin_do_not_disconnect.render(target);
}
- self.text.render(target);
}
}
diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/text_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/text_screen.rs
index dd970a68..8c2879af 100644
--- a/core/embed/rust/src/ui/layout_eckhart/firmware/text_screen.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/firmware/text_screen.rs
@@ -11,14 +11,14 @@ use crate::{
Component, Event, EventCtx, FormattedText, Label, Paginate, TextLayout,
},
flow::Swipable,
- geometry::{Insets, Offset, Rect},
+ geometry::{Offset, Rect},
shape::Renderer,
util::Pager,
},
};
use super::{
- theme::{self, ScreenBackground, SIDE_INSETS},
+ theme::{self, ScreenBackground, CONTENT_INSETS_NO_HEADER, SIDE_INSETS},
ActionBar, ActionBarMsg, FidoAccountName, FidoCredential, Header, HeaderMsg, Hint,
};
@@ -55,7 +55,6 @@ where
const SUBTITLE_HEIGHT: i16 = 44;
const SUBTITLE_DOUBLE_HEIGHT: i16 = 76;
const SUBTITLE_STYLE: TextStyle = theme::TEXT_MEDIUM_EXTRA_LIGHT;
- const CONTENT_INSETS_NO_HEADER: Insets = Insets::top(38);
pub fn new(content: T) -> Self {
Self {
@@ -177,11 +176,10 @@ where
};
// Introduce side insets + top padding if the header is not present
- content_area = content_area.inset(SIDE_INSETS);
content_area = if self.header.is_none() {
- content_area.inset(Self::CONTENT_INSETS_NO_HEADER)
+ content_area.inset(CONTENT_INSETS_NO_HEADER)
} else {
- content_area
+ content_area.inset(SIDE_INSETS)
};
self.content.place(content_area);
diff --git a/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs b/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
index ae6a5c5d..fc9afcc9 100644
--- a/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/theme/mod.rs
@@ -12,7 +12,7 @@ pub use firmware::*;
use crate::ui::{
component::text::TextStyle,
display::Color,
- geometry::{Grid, Insets, Offset, Rect},
+ geometry::{Grid, Insets, Offset, Point, Rect},
util::include_icon,
};
@@ -212,3 +212,10 @@ pub const TEXT_SMALL_GREY_EXTRA_LIGHT: TextStyle = TextStyle {
text_color: GREY_EXTRA_LIGHT,
..TEXT_SMALL
};
+
+/// Where to place main content if no Header is used on the Screen
+pub const CONTENT_INSETS_NO_HEADER: Insets = Insets::new(38, PADDING, ACTION_BAR_HEIGHT, PADDING);
+/// Where to place main text of progress screens of bootloader and firmware
+pub const PROGRESS_TEXT_ORIGIN: Point = super::constant::SCREEN
+ .top_left()
+ .ofs(Offset::new(PADDING, CONTENT_INSETS_NO_HEADER.top));
diff --git a/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs b/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
index b434b0ef..6b87b149 100644
--- a/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
+++ b/core/embed/rust/src/ui/layout_eckhart/ui_bootloader.rs
@@ -39,7 +39,6 @@ pub type BootloaderString = String<128>;
const RESTART_MESSAGE: &str = "Restart";
const SCREEN: Rect = UIEckhart::SCREEN;
-const PROGRESS_TEXT_ORIGIN: Point = SCREEN.top_left().ofs(Offset::new(theme::PADDING, 38));
const PROGRESS_WAIT_HEIGHT: i16 = 70;
const PROGRESS_WAIT_ORIGIN: Point = SCREEN
.bottom_left()
@@ -62,14 +61,13 @@ impl UIEckhart {
}
display::sync();
- let mut label = Label::new(text.into(), Alignment::Start, TEXT_NORMAL);
- let mut wait_msg =
- Label::new(wait_msg.into(), Alignment::Center, TEXT_SMALL_GREY).vertically_centered();
+ let mut label = Label::left_aligned(text.into(), TEXT_NORMAL);
+ let mut wait_msg = Label::centered(wait_msg.into(), TEXT_SMALL_GREY).vertically_centered();
render_on_display(None, Some(BLD_BG), |target| {
render_loader(loader_progress, border, target);
label.place(Rect::from_top_left_and_size(
- PROGRESS_TEXT_ORIGIN,
+ theme::PROGRESS_TEXT_ORIGIN,
Offset::new(
SCREEN.width() - 2 * theme::PADDING,
4 * FONT_SATOSHI_REGULAR_38.text_height(),
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.