From 73c493d118f5103b39c51576a9fbc558fdf6a1c3 Mon Sep 17 00:00:00 2001 From: grdddj Date: Thu, 4 May 2023 16:00:00 +0200 Subject: [PATCH] chore(core/rust): tracing and other smaller improvements/clippy lints --- core/embed/rust/src/lib.rs | 5 + core/embed/rust/src/maybe_trace.rs | 6 +- core/embed/rust/src/trace.rs | 3 +- core/embed/rust/src/ui/component/base.rs | 2 +- core/embed/rust/src/ui/component/marquee.rs | 11 +++ core/embed/rust/src/ui/component/paginated.rs | 2 + .../rust/src/ui/component/text/formatted.rs | 2 +- .../rust/src/ui/component/text/layout.rs | 78 ++++++++++----- .../rust/src/ui/component/text/paragraphs.rs | 4 +- .../rust/src/ui/display/loader/starry.rs | 4 +- core/embed/rust/src/ui/display/mod.rs | 84 ++++++++++------ core/embed/rust/src/ui/geometry.rs | 42 ++++---- .../rust/src/ui/model_tr/bootloader/mod.rs | 4 +- .../rust/src/ui/model_tr/component/button.rs | 4 +- .../rust/src/ui/model_tr/component/dialog.rs | 8 +- .../rust/src/ui/model_tr/component/frame.rs | 2 +- .../src/ui/model_tr/component/result_popup.rs | 8 +- core/embed/rust/src/ui/model_tr/layout.rs | 95 ------------------- core/embed/rust/src/ui/model_tr/theme.rs | 3 + .../rust/src/ui/model_tt/component/button.rs | 4 +- .../rust/src/ui/model_tt/component/frame.rs | 9 +- .../ui/model_tt/component/keyboard/bip39.rs | 8 +- .../model_tt/component/keyboard/passphrase.rs | 7 +- .../src/ui/model_tt/component/keyboard/pin.rs | 11 +++ .../ui/model_tt/component/keyboard/slip39.rs | 9 +- .../rust/src/ui/model_tt/component/page.rs | 2 +- core/embed/rust/src/ui/model_tt/constant.rs | 2 - core/embed/rust/src/ui/model_tt/layout.rs | 82 +++++++--------- 28 files changed, 254 insertions(+), 247 deletions(-) diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index 1ff0a5dd3a..49b874fd0a 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -2,6 +2,8 @@ #![deny(clippy::all)] #![allow(clippy::new_without_default)] #![deny(unsafe_op_in_unsafe_fn)] +// Allowing dead code not to cause a lot of warnings when building for a specific target +// (when building for TR, a lot of code only used in TT would get marked as unused). #![allow(dead_code)] #![feature(lang_items)] #![feature(optimize_attribute)] @@ -40,6 +42,9 @@ fn panic_debug(panic_info: &core::panic::PanicInfo) -> ! { if let Some(location) = panic_info.location() { let file = location.file(); + print!(file); + print!(":"); + println!(inttostr!(location.line())); trezorhal::fatal_error::__fatal_error("", "rs", file, location.line(), ""); } else { trezorhal::fatal_error::__fatal_error("", "rs", "", 0, ""); diff --git a/core/embed/rust/src/maybe_trace.rs b/core/embed/rust/src/maybe_trace.rs index 8519aabdfc..2eb2a7c8c6 100644 --- a/core/embed/rust/src/maybe_trace.rs +++ b/core/embed/rust/src/maybe_trace.rs @@ -1,5 +1,5 @@ #[cfg(feature = "ui_debug")] -mod maybe_trace { +mod maybe_trace_private { use crate::trace::Trace; pub trait MaybeTrace: Trace {} @@ -7,9 +7,9 @@ mod maybe_trace { } #[cfg(not(feature = "ui_debug"))] -mod maybe_trace { +mod maybe_trace_private { pub trait MaybeTrace {} impl MaybeTrace for T {} } -pub use maybe_trace::MaybeTrace; +pub use maybe_trace_private::MaybeTrace; diff --git a/core/embed/rust/src/trace.rs b/core/embed/rust/src/trace.rs index 8c8e2e3ccf..5c844f1630 100644 --- a/core/embed/rust/src/trace.rs +++ b/core/embed/rust/src/trace.rs @@ -112,7 +112,7 @@ impl JsonTracer { impl ListTracer for JsonTracer { fn child(&mut self, value: &dyn Trace) { - ListTracer::in_child(self, &mut |t| value.trace(t)); + ListTracer::in_child(self, &|t| value.trace(t)); } fn int(&mut self, i: i64) { @@ -196,7 +196,6 @@ pub trait Trace { #[cfg(test)] pub mod tests { - extern crate serde_json; use serde_json::Value; use super::*; diff --git a/core/embed/rust/src/ui/component/base.rs b/core/embed/rust/src/ui/component/base.rs index 93bc8a6a09..cb108699f5 100644 --- a/core/embed/rust/src/ui/component/base.rs +++ b/core/embed/rust/src/ui/component/base.rs @@ -215,7 +215,7 @@ where U: crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { - t.in_list("children", &mut |l| { + t.in_list("children", &|l| { l.child(&self.0); l.child(&self.1); }); diff --git a/core/embed/rust/src/ui/component/marquee.rs b/core/embed/rust/src/ui/component/marquee.rs index ff9cf73a05..0b4292373e 100644 --- a/core/embed/rust/src/ui/component/marquee.rs +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -6,6 +6,7 @@ use crate::{ display, display::{Color, Font}, geometry::Rect, + util::animation_disabled, }, }; @@ -54,6 +55,11 @@ where } pub fn start(&mut self, ctx: &mut EventCtx, now: Instant) { + // Not starting if animations are disabled. + if animation_disabled() { + return; + } + if let State::Initial = self.state { let text_width = self.font.text_width(self.text.as_ref()); let max_offset = self.area.width() - text_width; @@ -150,6 +156,11 @@ where } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + // Not doing anything if animations are disabled. + if animation_disabled() { + return None; + } + let now = Instant::now(); if let Event::Timer(token) = event { diff --git a/core/embed/rust/src/ui/component/paginated.rs b/core/embed/rust/src/ui/component/paginated.rs index 563df5a449..db0a516e08 100644 --- a/core/embed/rust/src/ui/component/paginated.rs +++ b/core/embed/rust/src/ui/component/paginated.rs @@ -29,7 +29,9 @@ pub enum PageMsg { } pub trait Paginate { + /// How many pages of content are there in total? fn page_count(&mut self) -> usize; + /// Navigate to the given page. fn change_page(&mut self, active_page: usize); } diff --git a/core/embed/rust/src/ui/component/text/formatted.rs b/core/embed/rust/src/ui/component/text/formatted.rs index 79a882b62c..911bc0976f 100644 --- a/core/embed/rust/src/ui/component/text/formatted.rs +++ b/core/embed/rust/src/ui/component/text/formatted.rs @@ -155,7 +155,7 @@ where use core::cell::Cell; let fit: Cell> = Cell::new(None); t.component("FormattedText"); - t.in_list("text", &mut |l| { + t.in_list("text", &|l| { let result = self.layout_content(&mut TraceSink(l)); fit.set(Some(result)); }); diff --git a/core/embed/rust/src/ui/component/text/layout.rs b/core/embed/rust/src/ui/component/text/layout.rs index 13c9365ff2..f6d08d85f2 100644 --- a/core/embed/rust/src/ui/component/text/layout.rs +++ b/core/embed/rust/src/ui/component/text/layout.rs @@ -1,7 +1,6 @@ -use super::iter::GlyphMetrics; use crate::ui::{ display, - display::{toif::Icon, Color, Font}, + display::{toif::Icon, Color, Font, GlyphMetrics}, geometry::{Alignment, Dimensions, Offset, Point, Rect, BOTTOM_LEFT}, }; @@ -55,9 +54,9 @@ pub struct TextLayout { #[derive(Copy, Clone)] pub struct TextStyle { - /// Text font ID. Can be overridden by `Op::Font`. + /// Text font ID. pub text_font: Font, - /// Text color. Can be overridden by `Op::Color`. + /// Text color. pub text_color: Color, /// Background color. pub background_color: Color, @@ -161,16 +160,19 @@ impl TextLayout { self } + /// Baseline `Point` where we are starting to draw the text. pub fn initial_cursor(&self) -> Point { let font = &self.style.text_font; self.bounds.top_left() + Offset::y(font.text_max_height() - font.text_baseline() + self.padding_top) } + /// Trying to fit the content on the current screen. pub fn fit_text(&self, text: &str) -> LayoutFit { self.layout_text(text, &mut self.initial_cursor(), &mut TextNoOp) } + /// Draw as much text as possible on the current screen. pub fn render_text(&self, text: &str) { self.layout_text(text, &mut self.initial_cursor(), &mut TextRenderer); } @@ -218,6 +220,9 @@ impl TextLayout { } } + /// Loop through the `text` and try to fit it on the current screen, + /// reporting events to `sink`, which may do something with them (e.g. draw + /// on screen). pub fn layout_text( &self, text: &str, @@ -225,11 +230,10 @@ impl TextLayout { sink: &mut dyn LayoutSink, ) -> LayoutFit { let init_cursor = *cursor; - let bottom = (self.bounds.y1 - self.padding_bottom).max(self.bounds.y0); let mut remaining_text = text; // Check if bounding box is high enough for at least one line. - if cursor.y > bottom { + if cursor.y > self.bottom_y() { sink.out_of_bounds(); return LayoutFit::OutOfBounds { processed_chars: 0, @@ -244,6 +248,7 @@ impl TextLayout { ) && self.continues_from_prev_page { sink.prev_page_ellipsis(*cursor, self); + // Move the cursor to the right, always the same distance cursor.x += self.style.prev_page_ellipsis_width(); } @@ -271,7 +276,12 @@ impl TextLayout { }; // Report the span at the cursor position. - sink.text(*cursor, self, &remaining_text[..span.length]); + // Not doing it when the span length is 0, as that + // means we encountered a newline/line-break, which we do not draw. + // Line-breaks are reported later. + if span.length > 0 { + sink.text(*cursor, self, &remaining_text[..span.length]); + } // Continue with the rest of the remaining_text. remaining_text = &remaining_text[span.length + span.skip_next_chars..]; @@ -287,7 +297,8 @@ impl TextLayout { sink.hyphen(*cursor, self); } // Check the amount of vertical space we have left. - if cursor.y + span.advance.y > bottom { + if cursor.y + span.advance.y > self.bottom_y() { + // Not enough space on this page. if !remaining_text.is_empty() { // Append ellipsis to indicate more content is available, but only if we // haven't already appended a hyphen. @@ -329,6 +340,7 @@ impl TextLayout { } } + /// Overall height of the content, including paddings. fn layout_height(&self, init_cursor: Point, end_cursor: Point) -> i16 { self.padding_top + self.style.text_font.text_height() @@ -336,7 +348,8 @@ impl TextLayout { + self.padding_bottom } - fn bottom_y(&self) -> i16 { + /// Y coordinate of the bottom of the available space/bounds + pub fn bottom_y(&self) -> i16 { (self.bounds.y1 - self.padding_bottom).max(self.bounds.y0) } } @@ -351,6 +364,9 @@ impl Dimensions for TextLayout { } } +/// Whether we can fit content on the current screen. +/// Knows how many characters got processed and how high the content is. +#[derive(Clone, Copy, PartialEq, Eq)] pub enum LayoutFit { /// Entire content fits. Vertical size is returned in `height`. Fitting { processed_chars: usize, height: i16 }, @@ -359,6 +375,7 @@ pub enum LayoutFit { } impl LayoutFit { + /// How high is the processed/fitted content. pub fn height(&self) -> i16 { match self { LayoutFit::Fitting { height, .. } => *height, @@ -368,24 +385,36 @@ impl LayoutFit { } /// Visitor for text segment operations. +/// Defines responses for certain kind of events encountered +/// when processing the content. pub trait LayoutSink { + /// Text should be processed. fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {} + /// Hyphen at the end of line. fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {} + /// Ellipsis at the end of the page. fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {} + /// Ellipsis at the beginning of the page. fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {} + /// Line break - a newline. fn line_break(&mut self, _cursor: Point) {} + /// Content cannot fit on the screen. fn out_of_bounds(&mut self) {} } +/// `LayoutSink` without any functionality. +/// Used to consume events when counting pages +/// or navigating to a certain page number. pub struct TextNoOp; impl LayoutSink for TextNoOp {} +/// `LayoutSink` for rendering the content. pub struct TextRenderer; impl LayoutSink for TextRenderer { fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) { - display::text( + display::text_left( cursor, text, layout.style.text_font, @@ -395,7 +424,7 @@ impl LayoutSink for TextRenderer { } fn hyphen(&mut self, cursor: Point, layout: &TextLayout) { - display::text( + display::text_left( cursor, "-", layout.style.text_font, @@ -405,15 +434,16 @@ impl LayoutSink for TextRenderer { } fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) { - if let Some(icon) = layout.style.ellipsis_icon { + if let Some((icon, margin)) = layout.style.ellipsis_icon { + let bottom_left = cursor + Offset::new(margin, 1); icon.draw( - cursor, + bottom_left, BOTTOM_LEFT, layout.style.ellipsis_color, layout.style.background_color, ); } else { - display::text( + display::text_left( cursor, ELLIPSIS, layout.style.text_font, @@ -424,7 +454,7 @@ impl LayoutSink for TextRenderer { } fn prev_page_ellipsis(&mut self, cursor: Point, layout: &TextLayout) { - if let Some(icon) = layout.style.prev_page_ellipsis_icon { + if let Some((icon, _margin)) = layout.style.prev_page_ellipsis_icon { icon.draw( cursor, BOTTOM_LEFT, @@ -432,7 +462,7 @@ impl LayoutSink for TextRenderer { layout.style.background_color, ); } else { - display::text( + display::text_left( cursor, ELLIPSIS, layout.style.text_font, @@ -454,23 +484,23 @@ pub mod trace { impl LayoutSink for TraceSink<'_> { fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &str) { - self.0.string(&text); + self.0.string(text); } fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string(&"-"); + self.0.string("-"); } fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string(&ELLIPSIS); + self.0.string(ELLIPSIS); } fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { - self.0.string(&ELLIPSIS); + self.0.string(ELLIPSIS); } fn line_break(&mut self, _cursor: Point) { - self.0.string(&"\n"); + self.0.string("\n"); } } } @@ -507,6 +537,8 @@ impl<'a> Op<'a> { } } +/// Carries info about the content that was processed +/// on the current line. #[derive(Debug, PartialEq, Eq)] struct Span { /// How many characters from the input text this span is laying out. @@ -603,7 +635,7 @@ impl Span { } found_any_whitespace = true; } else if span_width + char_width > max_width { - // Return the last breakpoint. + // Cannot fit on this line. Return the last breakpoint. return line; } else { let have_space_for_break = @@ -625,7 +657,7 @@ impl Span { span_width += char_width; } - // The whole text is fitting. + // The whole text is fitting on the current line. Self { length: text.len(), advance: Offset::x(span_width), diff --git a/core/embed/rust/src/ui/component/text/paragraphs.rs b/core/embed/rust/src/ui/component/text/paragraphs.rs index 54619c3018..0409465382 100644 --- a/core/embed/rust/src/ui/component/text/paragraphs.rs +++ b/core/embed/rust/src/ui/component/text/paragraphs.rs @@ -239,13 +239,13 @@ pub mod trace { impl crate::trace::Trace for Paragraphs { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.string("component", "Paragraphs"); - t.in_list("paragraphs", &mut |par_list| { + t.in_list("paragraphs", &|par_list| { Self::foreach_visible( &self.source, &self.visible, self.offset, &mut |layout, content| { - par_list.in_list(&mut |par| { + par_list.in_list(&|par| { layout.layout_text( content, &mut layout.initial_cursor(), diff --git a/core/embed/rust/src/ui/display/loader/starry.rs b/core/embed/rust/src/ui/display/loader/starry.rs index 2dbdd2f5f4..345d63a943 100644 --- a/core/embed/rust/src/ui/display/loader/starry.rs +++ b/core/embed/rust/src/ui/display/loader/starry.rs @@ -1,6 +1,6 @@ use crate::ui::{ constant::{screen, LOADER_OUTER}, - display::{rect_fill, rect_fill_rounded, rect_fill_rounded1, Color, Icon}, + display::{rect_fill, rect_fill_rounded, Color, Icon}, geometry::{Offset, Point, Rect, CENTER}, }; use core::f32::consts::SQRT_2; @@ -18,7 +18,7 @@ fn star_small(center: Point, fg: Color, _bg: Color) { fn star_medium(center: Point, fg: Color, bg: Color) { let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_MEDIUM)); - rect_fill_rounded1(r, fg, bg); + rect_fill_rounded(r, fg, bg, 1); } fn star_large(center: Point, fg: Color, bg: Color) { diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index a0ef90e5d4..6c31b5b153 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -15,6 +15,9 @@ use crate::trezorhal::{ }, uzlib::UZLIB_WINDOW_SIZE, }; +#[cfg(feature = "dma2d")] +use crate::ui::component::image::Image; + #[cfg(not(feature = "dma2d"))] use crate::ui::geometry::TOP_LEFT; @@ -23,12 +26,11 @@ use crate::{ trezorhal::{buffers, display, time, uzlib::UzlibContext}, ui::lerp::Lerp, }; -use core::slice; -#[cfg(feature = "dma2d")] -use crate::ui::component::image::Image; +// Reexports pub use crate::ui::display::toif::Icon; -#[cfg(any(feature = "model_tt", feature = "model_tr"))] +pub use color::Color; +pub use font::{Font, Glyph, GlyphMetrics}; pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN}; pub fn backlight() -> u16 { @@ -58,6 +60,7 @@ pub fn fade_backlight_duration(target: u16, duration_ms: u32) { set_backlight(target as u16); } +/// Fill a whole rectangle with a specific color. pub fn rect_fill(r: Rect, fg_color: Color) { display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into()); } @@ -69,30 +72,54 @@ pub fn rect_stroke(r: Rect, fg_color: Color) { display::bar(r.x0 + r.width() - 1, r.y0, 1, r.height(), fg_color.into()); } +/// Draw a rectangle with rounded corners. pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) { - assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed)); - display::bar_radius( - r.x0, - r.y0, - r.width(), - r.height(), - fg_color.into(), - bg_color.into(), - radius, - ); + if radius == 1 { + rect_fill_rounded1(r, fg_color, bg_color); + } else { + assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed)); + display::bar_radius( + r.x0, + r.y0, + r.width(), + r.height(), + fg_color.into(), + bg_color.into(), + radius, + ); + } } -// Used on T1 only. -pub fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) { - display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into()); - let corners = [ - r.top_left(), - r.top_right() - Offset::x(1), - r.bottom_right() - Offset::uniform(1), - r.bottom_left() - Offset::y(1), - ]; - for p in corners.iter() { - display::bar(p.x, p.y, 1, 1, bg_color.into()); +/// Filling a rectangle with a rounding of 1 pixel - removing the corners. +fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) { + rect_fill(r, fg_color); + rect_fill_corners(r, bg_color); +} + +/// Creating a rectangular outline with a given radius/rounding. +pub fn rect_outline_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8) { + // Painting a bigger rectangle with FG and inner smaller with BG + // to create the outline. + let inner_r = r.shrink(1); + if radius == 1 { + rect_fill_rounded(r, fg_color, bg_color, 1); + rect_fill(inner_r, bg_color); + rect_fill_corners(inner_r, fg_color); + } else if radius == 2 { + rect_fill_rounded(r, fg_color, bg_color, 2); + rect_fill_rounded(inner_r, bg_color, fg_color, 1); + } else if radius == 4 { + rect_fill_rounded(r, fg_color, bg_color, 4); + rect_fill_rounded(inner_r, bg_color, fg_color, 2); + rect_fill_corners(inner_r, bg_color); + } +} + +/// Filling all four corners of a rectangle with a given color. +pub fn rect_fill_corners(r: Rect, fg_color: Color) { + for p in r.corner_points().iter() { + // This draws a 1x1 rectangle at the given point. + display::bar(p.x, p.y, 1, 1, fg_color.into()); } } @@ -114,7 +141,7 @@ impl<'a> TextOverlay<'a> { text, font, max_height: font.max_height(), - baseline: font.baseline(), + baseline: font.text_baseline(), } } @@ -801,7 +828,8 @@ pub fn dotted_line(start: Point, width: i16, color: Color) { } } -pub fn text(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) { +/// Display text left-aligned to a certain Point +pub fn text_left(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) { display::text( baseline.x, baseline.y, @@ -812,6 +840,7 @@ pub fn text(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: ); } +/// Display text centered around a certain Point pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) { let w = font.text_width(text); display::text( @@ -824,6 +853,7 @@ pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_ ); } +/// Display text right-alligned to a certain Point pub fn text_right(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) { let w = font.text_width(text); display::text( diff --git a/core/embed/rust/src/ui/geometry.rs b/core/embed/rust/src/ui/geometry.rs index bab90e4f7d..e873c4369a 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -202,6 +202,8 @@ impl From for Point { /// A rectangle in 2D space defined by the top-left point `x0`,`y0` and the /// bottom-right point `x1`,`y1`. +/// NOTE: bottom-right point is not included in the rectangle, it is outside of +/// it. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Rect { pub x0: i16, @@ -304,10 +306,12 @@ impl Rect { self.bottom_left().center(self.bottom_right()) } + /// Whether a `Point` is inside the `Rect`. pub const fn contains(&self, point: Point) -> bool { point.x >= self.x0 && point.x < self.x1 && point.y >= self.y0 && point.y < self.y1 } + /// Create a bigger `Rect` that contains both `self` and `other`. pub const fn union(&self, other: Self) -> Self { Self { x0: min(self.x0, other.x0), @@ -317,6 +321,8 @@ impl Rect { } } + /// Create a smaller `Rect` from the bigger one by moving + /// all the four sides closer to the center. pub const fn inset(&self, insets: Insets) -> Self { Self { x0: self.x0 + insets.left, @@ -335,24 +341,12 @@ impl Rect { } } - pub const fn cut_from_left(&self, width: i16) -> Self { - Self { - x0: self.x0, - y0: self.y0, - x1: self.x0 + width, - y1: self.y1, - } - } - - pub const fn cut_from_right(&self, width: i16) -> Self { - Self { - x0: self.x1 - width, - y0: self.y0, - x1: self.x1, - y1: self.y1, - } + /// Move all the sides closer to the center by the same distance. + pub const fn shrink(&self, size: i16) -> Self { + self.inset(Insets::uniform(size)) } + /// Split `Rect` into top and bottom, given the top one's `height`. pub const fn split_top(self, height: i16) -> (Self, Self) { let height = clamp(height, 0, self.height()); @@ -367,10 +361,12 @@ impl Rect { (top, bottom) } + /// Split `Rect` into top and bottom, given the bottom one's `height`. pub const fn split_bottom(self, height: i16) -> (Self, Self) { self.split_top(self.height() - height) } + /// Split `Rect` into left and right, given the left one's `width`. pub const fn split_left(self, width: i16) -> (Self, Self) { let width = clamp(width, 0, self.width()); @@ -385,6 +381,7 @@ impl Rect { (left, right) } + /// Split `Rect` into left and right, given the right one's `width`. pub const fn split_right(self, width: i16) -> (Self, Self) { self.split_left(self.width() - width) } @@ -406,6 +403,7 @@ impl Rect { } } + /// Moving `Rect` by the given offset. pub const fn translate(&self, offset: Offset) -> Self { Self { x0: self.x0 + offset.x, @@ -414,6 +412,16 @@ impl Rect { y1: self.y1 + offset.y, } } + + /// Get all four corner points. + pub fn corner_points(&self) -> [Point; 4] { + [ + self.top_left(), + self.top_right() - Offset::x(1), + self.bottom_right() - Offset::uniform(1), + self.bottom_left() - Offset::y(1), + ] + } } #[derive(Copy, Clone, PartialEq, Eq)] @@ -538,7 +546,7 @@ impl Grid { let cell_height = (self.area.height() - spacing_height) / nrows; // Not every area can be fully covered by equal-sized cells and spaces, there - // might be serveral pixels left unused. We'll distribute them by 1px to + // might be several pixels left unused. We'll distribute them by 1px to // the leftmost cells. let leftover_width = (self.area.width() - spacing_width) % ncols; let leftover_height = (self.area.height() - spacing_height) % nrows; diff --git a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs index cb4daf639d..bd650810de 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs @@ -19,7 +19,7 @@ use crate::ui::{ text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt}, Event, EventCtx, }, - constant::{screen, BACKLIGHT_NORMAL, WIDTH}, + constant::{screen, WIDTH}, display::{fade_backlight_duration, Color, Icon, TextOverlay}, event::ButtonEvent, geometry::{LinearPlacement, Offset, Rect, CENTER}, @@ -31,7 +31,7 @@ use crate::ui::{ theme::{bld_button_cancel, bld_button_default, BLD_BG, BLD_FG}, }, component::{Button, ButtonPos, ResultScreen}, - theme::{ICON_FAIL, ICON_SUCCESS, LOGO_EMPTY}, + theme::{BACKLIGHT_NORMAL, ICON_FAIL, ICON_SUCCESS, LOGO_EMPTY}, }, util::{from_c_array, from_c_str}, }; diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index 5ba010d25e..3fbcdc794b 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -132,12 +132,12 @@ where ButtonContent::Text(text) => { let background_color = style.text_color.negate(); if style.border_horiz { - display::rect_fill_rounded1(self.area, background_color, theme::BG); + display::rect_fill_rounded(self.area, background_color, theme::BG, 1); } else { display::rect_fill(self.area, background_color) } - display::text( + display::text_left( self.baseline, text.as_ref(), style.font, diff --git a/core/embed/rust/src/ui/model_tr/component/dialog.rs b/core/embed/rust/src/ui/model_tr/component/dialog.rs index d5bbf1336b..e1c6f39210 100644 --- a/core/embed/rust/src/ui/model_tr/component/dialog.rs +++ b/core/embed/rust/src/ui/model_tr/component/dialog.rs @@ -83,7 +83,11 @@ where fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Dialog"); t.child("content", &self.content); - self.left_btn.as_ref().map(|b| t.child("left", b)); - self.right_btn.as_ref().map(|b| t.child("right", b)); + if let Some(b) = self.left_btn.as_ref() { + t.child("left", b) + } + if let Some(b) = self.right_btn.as_ref() { + t.child("right", b) + } } } diff --git a/core/embed/rust/src/ui/model_tr/component/frame.rs b/core/embed/rust/src/ui/model_tr/component/frame.rs index c0a2fc6e36..4151ead745 100644 --- a/core/embed/rust/src/ui/model_tr/component/frame.rs +++ b/core/embed/rust/src/ui/model_tr/component/frame.rs @@ -52,7 +52,7 @@ where } fn paint(&mut self) { - display::text( + display::text_left( self.area.bottom_left() - Offset::y(2), self.title.as_ref(), Font::BOLD, diff --git a/core/embed/rust/src/ui/model_tr/component/result_popup.rs b/core/embed/rust/src/ui/model_tr/component/result_popup.rs index f7b79390b3..4a2b18ece4 100644 --- a/core/embed/rust/src/ui/model_tr/component/result_popup.rs +++ b/core/embed/rust/src/ui/model_tr/component/result_popup.rs @@ -160,8 +160,12 @@ impl crate::trace::Trace for ResultPopup { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("ResultPopup"); t.child("text", &self.text); - self.button.as_ref().map(|b| t.child("button", b)); - self.headline.as_ref().map(|h| t.child("headline", h)); + if let Some(b) = self.button.as_ref() { + t.child("button", b) + } + if let Some(h) = self.headline.as_ref() { + t.child("headline", h) + } t.child("result_anim", &self.result_anim); } } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index a78fbbee50..177dc3c9f2 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -144,98 +144,3 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm text.""" Qstr::MP_QSTR_confirm_text => obj_fn_kw!(0, new_confirm_text).as_obj(), }; - -#[cfg(test)] -mod tests { - extern crate json; - - use crate::{ - trace::tests::trace, - ui::{ - component::Component, - model_tr::{ - component::{Dialog, DialogMsg}, - constant, - }, - }, - }; - - use super::*; - - impl ComponentMsgObj for Dialog - where - T: ComponentMsgObj, - U: AsRef, - { - fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { - match msg { - DialogMsg::Content(c) => self.inner().msg_try_into_obj(c), - DialogMsg::LeftClicked => Ok(CANCELLED.as_obj()), - DialogMsg::RightClicked => Ok(CONFIRMED.as_obj()), - } - } - } - - #[test] - fn trace_example_layout() { - let mut layout = Dialog::new( - FormattedText::new( - theme::TEXT_NORMAL, - theme::FORMATTED, - "Testing text layout, with some text, and some more text. And {param}", - ) - .with("param", "parameters!"), - Some(Button::with_text( - ButtonPos::Left, - "Left", - theme::button_cancel(), - )), - Some(Button::with_text( - ButtonPos::Right, - "Right", - theme::button_default(), - )), - ); - layout.place(constant::screen()); - assert_eq!( - trace(&layout), - r#" left: