diff --git a/core/embed/rust/src/ui/component/text/layout.rs b/core/embed/rust/src/ui/component/text/layout.rs index dbf8481b8a..a892113985 100644 --- a/core/embed/rust/src/ui/component/text/layout.rs +++ b/core/embed/rust/src/ui/component/text/layout.rs @@ -59,6 +59,11 @@ pub struct TextStyle { /// Foreground color used for drawing the ellipsis. pub ellipsis_color: Color, + /// Optional icon shown as ellipsis. + pub ellipsis_icon: Option<&'static [u8]>, + /// Optional icon to signal content continues from previous page. + pub prev_page_icon: Option<&'static [u8]>, + /// Specifies which line-breaking strategy to use. pub line_breaking: LineBreaking, /// Specifies what to do at the end of the page. @@ -81,6 +86,8 @@ impl TextStyle { ellipsis_color, line_breaking: LineBreaking::BreakAtWhitespace, page_breaking: PageBreaking::CutAndInsertEllipsis, + ellipsis_icon: None, + prev_page_icon: None, } } @@ -93,6 +100,18 @@ impl TextStyle { self.page_breaking = page_breaking; self } + + /// Adding optional icon shown instead of "..." ellipsis. + pub const fn with_ellipsis_icon(mut self, icon: &'static [u8]) -> Self { + self.ellipsis_icon = Some(icon); + self + } + + /// Adding optional icon signalling content continues from previous page. + pub const fn with_prev_page_icon(mut self, icon: &'static [u8]) -> Self { + self.prev_page_icon = Some(icon); + self + } } impl TextLayout { @@ -374,13 +393,24 @@ impl LayoutSink for TextRenderer { } fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) { - display::text( - cursor, - "...", - layout.style.text_font, - layout.style.ellipsis_color, - layout.style.background_color, - ); + if let Some(icon) = layout.style.ellipsis_icon { + // Icon should end little below the cursor's baseline + let bottom_left = cursor + Offset::y(2); + display::icon_bottom_left( + bottom_left, + icon, + layout.style.ellipsis_color, + layout.style.background_color, + ); + } else { + display::text( + cursor, + "...", + layout.style.text_font, + layout.style.ellipsis_color, + layout.style.background_color, + ); + } } } diff --git a/core/embed/rust/src/ui/display/icon.rs b/core/embed/rust/src/ui/display/icon.rs index 8eb1687211..9f6fba24dd 100644 --- a/core/embed/rust/src/ui/display/icon.rs +++ b/core/embed/rust/src/ui/display/icon.rs @@ -8,7 +8,7 @@ use super::{icon_rect, toif_info_ensure, Color}; /// Storing the icon together with its name /// Needs to be a tuple-struct, so it can be made `const` #[derive(Debug, Clone, Copy)] -pub struct IconAndName(&'static [u8], &'static str); +pub struct IconAndName(pub &'static [u8], pub &'static str); impl IconAndName { pub const fn new(icon: &'static [u8], name: &'static str) -> Self { diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index 6e845dd589..2f7b609eed 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -93,6 +93,7 @@ pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) } } +/// Draws icon given its top left corner. /// NOTE: Cannot start at odd x-coordinate. In this case icon is shifted 1px /// left. pub fn icon_top_left(top_left: Point, data: &[u8], fg_color: Color, bg_color: Color) { @@ -108,6 +109,20 @@ pub fn icon_top_left(top_left: Point, data: &[u8], fg_color: Color, bg_color: Co ); } +/// Draws icon given its bottom left corner. +pub fn icon_bottom_left(bottom_left: Point, data: &[u8], fg_color: Color, bg_color: Color) { + let (toif_size, _toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); + icon_top_left( + Point { + x: bottom_left.x, + y: bottom_left.y - toif_size.y, + }, + data, + fg_color, + bg_color, + ); +} + /// Display icon given a center Point. pub fn icon(center: Point, data: &[u8], fg_color: Color, bg_color: Color) { let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH); diff --git a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs index 5993a9ec1a..18adb93c57 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs @@ -79,7 +79,8 @@ impl Page { theme::BG, theme::FG, theme::FG, - ); + ) + .with_ellipsis_icon(theme::ICON_NEXT_PAGE.0); Self { ops: Vec::new(), layout: TextLayout::new(style), diff --git a/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs b/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs index ef9b65bae4..0aa74a7e84 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow_pages_poc_helpers.rs @@ -104,6 +104,11 @@ pub struct TextStyle { /// Foreground color used for drawing the ellipsis. pub ellipsis_color: Color, + /// Optional icon shown as ellipsis. + pub ellipsis_icon: Option<&'static [u8]>, + /// Optional icon to signal content continues from previous page. + pub prev_page_icon: Option<&'static [u8]>, + /// Specifies which line-breaking strategy to use. pub line_breaking: LineBreaking, /// Specifies what to do at the end of the page. @@ -130,8 +135,22 @@ impl TextStyle { line_breaking: LineBreaking::BreakAtWhitespace, page_breaking: PageBreaking::CutAndInsertEllipsis, line_alignment: LineAlignment::Left, + ellipsis_icon: None, + prev_page_icon: None, } } + + /// Adding optional icon shown instead of "..." ellipsis. + pub const fn with_ellipsis_icon(mut self, icon: &'static [u8]) -> Self { + self.ellipsis_icon = Some(icon); + self + } + + /// Adding optional icon signalling content continues from previous page. + pub const fn with_prev_page_icon(mut self, icon: &'static [u8]) -> Self { + self.prev_page_icon = Some(icon); + self + } } impl TextLayout { @@ -248,7 +267,6 @@ impl TextLayout { let text_len = to_display.length; let start = text.len() - text_len; let to_really_display = &text[start..]; - // let to_really_display = text.text[text.start..text.end].to_string(); self.layout_text(to_really_display, cursor, sink) } else if let Op::Icon(icon) = op { self.layout_icon(icon, cursor, sink) @@ -524,13 +542,24 @@ impl LayoutSink for TextRenderer { } fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) { - display::text( - cursor, - "...", - layout.style.text_font, - layout.style.ellipsis_color, - layout.style.background_color, - ); + if let Some(icon) = layout.style.ellipsis_icon { + // Icon should end little below the cursor's baseline + let bottom_left = cursor + Offset::y(2); + display::icon_bottom_left( + bottom_left, + icon, + layout.style.ellipsis_color, + layout.style.background_color, + ); + } else { + display::text( + cursor, + "...", + layout.style.text_font, + layout.style.ellipsis_color, + layout.style.background_color, + ); + } } fn icon(&mut self, cursor: Point, layout: &TextLayout, icon: Icon) { diff --git a/core/embed/rust/src/ui/model_tr/theme.rs b/core/embed/rust/src/ui/model_tr/theme.rs index d967d11bab..e4395ccce6 100644 --- a/core/embed/rust/src/ui/model_tr/theme.rs +++ b/core/embed/rust/src/ui/model_tr/theme.rs @@ -13,10 +13,14 @@ pub const FONT_HEADER: Font = Font::BOLD; pub const FONT_CHOICE_ITEMS: Font = Font::NORMAL; // Text constants. -pub const TEXT_NORMAL: TextStyle = TextStyle::new(Font::NORMAL, FG, BG, FG, FG); -pub const TEXT_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, FG, FG); -pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, FG, FG); -pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, FG, FG); +pub const TEXT_NORMAL: TextStyle = + TextStyle::new(Font::NORMAL, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0); +pub const TEXT_DEMIBOLD: TextStyle = + TextStyle::new(Font::DEMIBOLD, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0); +pub const TEXT_BOLD: TextStyle = + TextStyle::new(Font::BOLD, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0); +pub const TEXT_MONO: TextStyle = + TextStyle::new(Font::MONO, FG, BG, FG, FG).with_ellipsis_icon(ICON_NEXT_PAGE.0); pub const FORMATTED: FormattedFonts = FormattedFonts { normal: Font::NORMAL,