1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-03 12:00:59 +00:00

chore(core/rust): tracing and other smaller improvements/clippy lints

This commit is contained in:
grdddj 2023-05-04 16:00:00 +02:00 committed by Martin Milata
parent eee4c624f9
commit 73c493d118
28 changed files with 254 additions and 247 deletions

View File

@ -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, "");

View File

@ -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<T> MaybeTrace for T {}
}
pub use maybe_trace::MaybeTrace;
pub use maybe_trace_private::MaybeTrace;

View File

@ -112,7 +112,7 @@ impl<F: FnMut(&str)> JsonTracer<F> {
impl<F: FnMut(&str)> ListTracer for JsonTracer<F> {
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::*;

View File

@ -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);
});

View File

@ -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<Self::Msg> {
// Not doing anything if animations are disabled.
if animation_disabled() {
return None;
}
let now = Instant::now();
if let Event::Timer(token) = event {

View File

@ -29,7 +29,9 @@ pub enum PageMsg<T, U> {
}
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);
}

View File

@ -155,7 +155,7 @@ where
use core::cell::Cell;
let fit: Cell<Option<LayoutFit>> = 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));
});

View File

@ -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),

View File

@ -239,13 +239,13 @@ pub mod trace {
impl<T: ParagraphSource> crate::trace::Trace for Paragraphs<T> {
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(),

View File

@ -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) {

View File

@ -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(

View File

@ -202,6 +202,8 @@ impl From<Offset> 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;

View File

@ -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},
};

View File

@ -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,

View File

@ -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)
}
}
}

View File

@ -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,

View File

@ -160,8 +160,12 @@ impl<S: ParagraphStrType> crate::trace::Trace for ResultPopup<S> {
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);
}
}

View File

@ -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<T, U> ComponentMsgObj for Dialog<T, U>
where
T: ComponentMsgObj,
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
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#"<Dialog content:<Text content:Testing text layout,
with some text, and
some more text. And p-
arameters! > left:<Button text:Left > right:<Button text:Right > >"#
)
}
#[test]
fn trace_layout_title() {
let mut layout = Frame::new(
"Please confirm",
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#"<Frame title:Please confirm content:<Dialog content:<Text content:Testing text layout,
with some text, and
some more text. And p-
arameters! > left:<Button text:Left > right:<Button text:Right > > >"#
)
}
}

View File

@ -6,6 +6,9 @@ use crate::ui::{
use super::component::{ButtonStyle, ButtonStyleSheet};
// Typical backlight values.
pub const BACKLIGHT_NORMAL: u16 = 150;
// Color palette.
pub const WHITE: Color = Color::rgb(255, 255, 255);
pub const BLACK: Color = Color::rgb(0, 0, 0);

View File

@ -200,7 +200,7 @@ impl<T> Button<T> {
let start_of_baseline = self.area.center()
+ Offset::new(-width / 2, height / 2)
+ Offset::y(Self::BASELINE_OFFSET);
display::text(
display::text_left(
start_of_baseline,
text,
style.font,
@ -575,7 +575,7 @@ impl IconText {
}
if use_text {
display::text(
display::text_left(
text_pos,
self.text,
style.font,

View File

@ -191,8 +191,13 @@ where
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Frame");
t.child("title", &self.title);
self.subtitle.as_ref().map(|s| t.child("subtitle", s));
self.button.as_ref().map(|b| t.child("button", b));
t.child("content", &self.content);
if let Some(subtitle) = &self.subtitle {
t.child("subtitle", subtitle);
}
if let Some(button) = &self.button {
t.child("button", button);
}
}
}

View File

@ -1,14 +1,14 @@
use crate::{
trezorhal::bip39,
ui::{
component::{Component, Event, EventCtx},
component::{text::common::TextBox, Component, Event, EventCtx},
display,
display::toif::Icon,
geometry::{Offset, Rect, CENTER},
model_tt::{
component::{
keyboard::{
common::{paint_pending_marker, MultiTapKeyboard, TextBox},
common::{paint_pending_marker, MultiTapKeyboard},
mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT},
},
Button, ButtonContent, ButtonMsg,
@ -110,7 +110,7 @@ impl Component for Bip39Input {
// Content starts in the left-center point, offset by 16px to the right and 8px
// to the bottom.
let text_baseline = area.top_left().center(area.bottom_left()) + Offset::new(16, 8);
display::text(
display::text_left(
text_baseline,
text,
style.font,
@ -121,7 +121,7 @@ impl Component for Bip39Input {
// Paint the rest of the suggested dictionary word.
if let Some(word) = self.suggested_word.and_then(|w| w.get(text.len()..)) {
let word_baseline = text_baseline + Offset::new(width, 0);
display::text(
display::text_left(
word_baseline,
word,
style.font,

View File

@ -1,11 +1,13 @@
use crate::ui::{
component::{base::ComponentExt, Child, Component, Event, EventCtx, Never},
component::{
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
},
display,
display::toif::Icon,
geometry::{Grid, Offset, Rect},
model_tt::component::{
button::{Button, ButtonContent, ButtonMsg},
keyboard::common::{paint_pending_marker, MultiTapKeyboard, TextBox},
keyboard::common::{paint_pending_marker, MultiTapKeyboard},
swipe::{Swipe, SwipeDirection},
theme, ScrollBar,
},
@ -378,5 +380,6 @@ impl Component for Input {
impl crate::trace::Trace for PassphraseKeyboard {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PassphraseKeyboard");
t.string("passphrase", self.passphrase());
}
}

View File

@ -468,5 +468,16 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PinKeyboard");
// So that debuglink knows the locations of the buttons
let mut digits_order: String<10> = String::new();
for btn in self.digit_btns.iter() {
let btn_content = btn.inner().content();
if let ButtonContent::Text(text) = btn_content {
unwrap!(digits_order.push_str(text));
}
}
t.string("digits_order", &digits_order);
t.string("pin", self.textbox.inner().pin());
t.bool("display_digits", self.textbox.inner().display_digits);
}
}

View File

@ -5,14 +5,17 @@ use heapless::String;
use crate::{
trezorhal::slip39,
ui::{
component::{Component, Event, EventCtx},
component::{
text::common::{TextBox, TextEdit},
Component, Event, EventCtx,
},
display,
display::toif::Icon,
geometry::{Offset, Rect, CENTER},
model_tt::{
component::{
keyboard::{
common::{paint_pending_marker, MultiTapKeyboard, TextBox, TextEdit},
common::{paint_pending_marker, MultiTapKeyboard},
mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT},
},
Button, ButtonContent, ButtonMsg,
@ -156,7 +159,7 @@ impl Component for Slip39Input {
.assert_if_debugging_ui("Text buffer is too small");
}
}
display::text(
display::text_left(
text_baseline,
text.as_str(),
style.font,

View File

@ -494,7 +494,7 @@ where
#[cfg(test)]
mod tests {
extern crate serde_json;
use serde_json;
use crate::{
trace::tests::trace,

View File

@ -9,8 +9,6 @@ pub const LOADER_OUTER: i16 = 60;
pub const LOADER_INNER: i16 = 42;
pub const LOADER_ICON_MAX_SIZE: i16 = 64;
pub const BACKLIGHT_NORMAL: i32 = 150;
pub const fn size() -> Offset {
Offset::new(WIDTH, HEIGHT)
}

View File

@ -1340,23 +1340,11 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title).unwrap().try_into().unwrap();
let description: StrBuffer = kwargs
.get(Qstr::MP_QSTR_description)
.unwrap()
.try_into()
.unwrap();
let button: StrBuffer = kwargs
.get(Qstr::MP_QSTR_button)
.unwrap()
.try_into()
.unwrap();
let dry_run: bool = kwargs
.get(Qstr::MP_QSTR_dry_run)
.unwrap()
.try_into()
.unwrap();
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false).unwrap();
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_DEMIBOLD, title).centered(),
@ -1391,11 +1379,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| {
let dry_run: bool = kwargs
.get(Qstr::MP_QSTR_dry_run)
.unwrap()
.try_into()
.unwrap();
let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
let title = if dry_run {
"SEED CHECK"
} else {
@ -1554,7 +1538,7 @@ extern "C" fn draw_welcome_screen() -> Obj {
screen.place(constant::screen());
display::sync();
screen.paint();
display::set_backlight(150); // BACKLIGHT_NORMAL
display::set_backlight(theme::BACKLIGHT_NORMAL);
Obj::const_none()
}
@ -1575,8 +1559,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Disable animations, debug builds only."""
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(),
/// def jpeg_info(data: bytes) -> (width: int, height: int, mcu_height: int):
/// """Get JPEG image dimensions."""
/// def jpeg_info(data: bytes) -> tuple[int, int, int]:
/// """Get JPEG image dimensions (width: int, height: int, mcu_height: int)."""
Qstr::MP_QSTR_jpeg_info => obj_fn_1!(upy_jpeg_info).as_obj(),
/// def jpeg_test(data: bytes) -> bool:
@ -1662,7 +1646,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_spending_details(
/// *,
/// account: str,
/// fee_rate: str | None = None,
/// fee_rate: str | None,
/// ) -> object:
/// """Show metadata when for outgoing transaction."""
Qstr::MP_QSTR_show_spending_details => obj_fn_kw!(0, new_show_spending_details).as_obj(),
@ -1671,8 +1655,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// *,
/// title: str,
/// value: str,
/// description: str | None = None,
/// subtitle: str | None = None,
/// description: str | None,
/// subtitle: str | None,
/// verb: str | None = None,
/// verb_cancel: str | None = None,
/// info_button: bool = False,
@ -1773,8 +1757,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_simple(
/// *,
/// title: str | None,
/// description: str,
/// button: str | None = None,
/// description: str = "",
/// button: str = "",
/// ) -> object:
/// """Simple dialog with text and one button."""
Qstr::MP_QSTR_show_simple => obj_fn_kw!(0, new_show_simple).as_obj(),
@ -1823,21 +1807,21 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// prompt: str,
/// max_len: int,
/// ) -> str | object:
/// """Passphrase input keyboard."""
/// """Passphrase input keyboard."""
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
/// def request_bip39(
/// *,
/// prompt: str,
/// ) -> str:
/// """BIP39 word input keyboard."""
/// """BIP39 word input keyboard."""
Qstr::MP_QSTR_request_bip39 => obj_fn_kw!(0, new_request_bip39).as_obj(),
/// def request_slip39(
/// *,
/// prompt: str,
/// ) -> str:
/// """SLIP39 word input keyboard."""
/// """SLIP39 word input keyboard."""
Qstr::MP_QSTR_request_slip39 => obj_fn_kw!(0, new_request_slip39).as_obj(),
/// def select_word(
@ -1846,7 +1830,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// description: str,
/// words: Iterable[str],
/// ) -> int:
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// """Select mnemonic word from three possibilities - seed check after backup. The
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, new_select_word).as_obj(),
@ -1855,7 +1839,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// title: str,
/// pages: Iterable[str],
/// ) -> object:
/// """Show mnemonic for backup. Expects the words pre-divided into individual pages."""
/// """Show mnemonic for backup. Expects the words pre-divided into individual pages."""
Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
/// def request_number(
@ -1866,7 +1850,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// max_count: int,
/// description: Callable[[int], str] | None = None,
/// ) -> object:
/// """Number input with + and - buttons, description, and info button."""
/// """Number input with + and - buttons, description, and info button."""
Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(),
/// def show_checklist(
@ -1876,8 +1860,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// active: int,
/// button: str,
/// ) -> object:
/// """Checklist of backup steps. Active index is highlighted, previous items have check
/// mark nex to them."""
/// """Checklist of backup steps. Active index is highlighted, previous items have check
/// mark next to them."""
Qstr::MP_QSTR_show_checklist => obj_fn_kw!(0, new_show_checklist).as_obj(),
/// def confirm_recovery(
@ -1886,41 +1870,41 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// description: str,
/// button: str,
/// dry_run: bool,
/// info_button: bool,
/// info_button: bool = False,
/// ) -> object:
/// """Device recovery homescreen."""
/// """Device recovery homescreen."""
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),
/// def select_word_count(
/// *,
/// dry_run: bool,
/// ) -> int | CANCELLED:
/// """Select mnemonic word count from (12, 18, 20, 24, 33)."""
/// ) -> int | str: # TT returns int
/// """Select mnemonic word count from (12, 18, 20, 24, 33)."""
Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_group_share_success(
/// *,
/// lines: Iterable[str]
/// ) -> int:
/// """Shown after successfully finishing a group."""
/// """Shown after successfully finishing a group."""
Qstr::MP_QSTR_show_group_share_success => obj_fn_kw!(0, new_show_group_share_success).as_obj(),
/// def show_remaining_shares(
/// *,
/// pages: Iterable[tuple[str, str]],
/// ) -> int:
/// """Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
/// """Shows SLIP39 state after info button is pressed on `confirm_recovery`."""
Qstr::MP_QSTR_show_remaining_shares => obj_fn_kw!(0, new_show_remaining_shares).as_obj(),
/// def show_progress(
/// *,
/// title: str,
/// indeterminate: bool = False,
/// description: str | None = None,
/// description: str = "",
/// ) -> object:
/// """Show progress loader. Please note that the number of lines reserved on screen for
/// """Show progress loader. Please note that the number of lines reserved on screen for
/// description is determined at construction time. If you want multiline descriptions
/// make sure the initial desciption has at least that amount of lines."""
/// make sure the initial description has at least that amount of lines."""
Qstr::MP_QSTR_show_progress => obj_fn_kw!(0, new_show_progress).as_obj(),
/// def show_progress_coinjoin(
@ -1930,7 +1914,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// time_ms: int = 0,
/// skip_first_paint: bool = False,
/// ) -> object:
/// """Show progress loader for coinjoin. Returns CANCELLED after a specified time when
/// """Show progress loader for coinjoin. Returns CANCELLED after a specified time when
/// time_ms timeout is passed."""
Qstr::MP_QSTR_show_progress_coinjoin => obj_fn_kw!(0, new_show_progress_coinjoin).as_obj(),
@ -1961,7 +1945,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
#[cfg(test)]
mod tests {
extern crate serde_json;
use serde_json;
use crate::{
trace::tests::trace,