diff --git a/core/embed/rust/src/ui/component/text/formatted.rs b/core/embed/rust/src/ui/component/text/formatted.rs index 2a7735797c..337db3ea68 100644 --- a/core/embed/rust/src/ui/component/text/formatted.rs +++ b/core/embed/rust/src/ui/component/text/formatted.rs @@ -34,7 +34,6 @@ impl FormattedText { pub fn with(mut self, key: &'static [u8], value: T) -> Self { if self.args.insert(key, value).is_err() { - // Map is full, ignore. #[cfg(feature = "ui_debug")] panic!("text args map is full"); } @@ -109,31 +108,11 @@ where } #[cfg(feature = "ui_debug")] -mod trace { - use crate::ui::geometry::Point; +pub mod trace { + use crate::ui::component::text::layout::trace::TraceSink; use super::*; - pub struct TraceSink<'a>(pub &'a mut dyn crate::trace::Tracer); - - impl<'a> LayoutSink for TraceSink<'a> { - fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &[u8]) { - self.0.bytes(text); - } - - fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string("-"); - } - - fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string("..."); - } - - fn line_break(&mut self, _cursor: Point) { - self.0.string("\n"); - } - } - pub struct TraceText<'a, F, T>(pub &'a FormattedText); impl<'a, F, T> crate::trace::Trace for TraceText<'a, F, T> @@ -239,12 +218,12 @@ impl<'a> Iterator for Tokenizer<'a> { #[cfg(test)] mod tests { + use std::array::IntoIter; + use super::*; #[test] fn tokenizer_yields_expected_tokens() { - use std::array::IntoIter; - assert!(Tokenizer::new(b"").eq(IntoIter::new([]))); assert!(Tokenizer::new(b"x").eq(IntoIter::new([Token::Literal(b"x")]))); assert!(Tokenizer::new(b"x\0y").eq(IntoIter::new([Token::Literal("x\0y".as_bytes())]))); diff --git a/core/embed/rust/src/ui/component/text/layout.rs b/core/embed/rust/src/ui/component/text/layout.rs index e5c184fd1e..e3c92e0574 100644 --- a/core/embed/rust/src/ui/component/text/layout.rs +++ b/core/embed/rust/src/ui/component/text/layout.rs @@ -204,6 +204,24 @@ impl TextLayout { processed_chars: text.len(), } } + + pub fn measure_ops_height(self, ops: &mut dyn Iterator) -> i32 { + // TODO: Return the bounding box in `LayoutFit` instead of computing it from the + // cursor. + let init_cursor = self.initial_cursor(); + let mut cursor = init_cursor; + self.layout_ops(ops, &mut cursor, &mut TextNoop); + cursor.y - init_cursor.y + self.text_font.line_height() + } + + pub fn measure_text_height(self, text: &[u8]) -> i32 { + // TODO: Return the bounding box in `LayoutFit` instead of computing it from the + // cursor. + let init_cursor = self.initial_cursor(); + let mut cursor = init_cursor; + self.layout_text(text, &mut cursor, &mut TextNoop); + cursor.y - init_cursor.y + self.text_font.line_height() + } } pub enum LayoutFit { @@ -258,6 +276,33 @@ impl LayoutSink for TextRenderer { } } +#[cfg(feature = "ui_debug")] +pub mod trace { + use crate::ui::geometry::Point; + + use super::*; + + pub struct TraceSink<'a>(pub &'a mut dyn crate::trace::Tracer); + + impl<'a> LayoutSink for TraceSink<'a> { + fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &[u8]) { + self.0.bytes(text); + } + + fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) { + self.0.string("-"); + } + + fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { + self.0.string("..."); + } + + fn line_break(&mut self, _cursor: Point) { + self.0.string("\n"); + } + } +} + #[derive(Copy, Clone, PartialEq, Eq)] pub enum Op<'a> { /// Render text with current color and font. @@ -269,7 +314,7 @@ pub enum Op<'a> { } impl<'a> Op<'a> { - fn skip_n_text_bytes( + pub fn skip_n_text_bytes( ops: impl Iterator>, skip_bytes: usize, ) -> impl Iterator> { diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index 3abfd39182..9f7b80df33 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -6,7 +6,7 @@ use crate::ui::{ geometry::{Dimensions, LinearLayout, Offset, Rect}, }; -use super::layout::{DefaultTextTheme, TextLayout, TextNoop, TextRenderer}; +use super::layout::{DefaultTextTheme, TextLayout, TextRenderer}; pub const MAX_PARAGRAPHS: usize = 6; @@ -71,6 +71,20 @@ where } } +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for Paragraphs +where + T: AsRef<[u8]>, +{ + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.open("Paragraphs"); + for paragraph in &self.list { + paragraph.trace(t); + } + t.close(); + } +} + pub struct Paragraph { content: T, layout: TextLayout, @@ -90,11 +104,10 @@ where } fn measure(content: &T, layout: TextLayout) -> Offset { - let mut cursor = layout.initial_cursor(); - layout.layout_text(content.as_ref(), &mut cursor, &mut TextNoop); - let width = layout.bounds.width(); - let height = cursor.y - layout.initial_cursor().y; - Offset::new(width, height) + Offset::new( + layout.bounds.width(), + layout.measure_text_height(content.as_ref()), + ) } } @@ -129,3 +142,37 @@ where ); } } + +#[cfg(feature = "ui_debug")] +pub mod trace { + use crate::ui::component::text::layout::trace::TraceSink; + + use super::*; + + pub struct TraceText<'a, T>(pub &'a Paragraph); + + impl<'a, T> crate::trace::Trace for TraceText<'a, T> + where + T: AsRef<[u8]>, + { + fn trace(&self, d: &mut dyn crate::trace::Tracer) { + self.0.layout.layout_text( + self.0.content.as_ref(), + &mut self.0.layout.initial_cursor(), + &mut TraceSink(d), + ); + } + } +} + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for Paragraph +where + T: AsRef<[u8]>, +{ + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + t.open("Paragraph"); + t.field("content", &trace::TraceText(self)); + t.close(); + } +}