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)] #![deny(clippy::all)]
#![allow(clippy::new_without_default)] #![allow(clippy::new_without_default)]
#![deny(unsafe_op_in_unsafe_fn)] #![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)] #![allow(dead_code)]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(optimize_attribute)] #![feature(optimize_attribute)]
@ -40,6 +42,9 @@ fn panic_debug(panic_info: &core::panic::PanicInfo) -> ! {
if let Some(location) = panic_info.location() { if let Some(location) = panic_info.location() {
let file = location.file(); let file = location.file();
print!(file);
print!(":");
println!(inttostr!(location.line()));
trezorhal::fatal_error::__fatal_error("", "rs", file, location.line(), ""); trezorhal::fatal_error::__fatal_error("", "rs", file, location.line(), "");
} else { } else {
trezorhal::fatal_error::__fatal_error("", "rs", "", 0, ""); trezorhal::fatal_error::__fatal_error("", "rs", "", 0, "");

View File

@ -1,5 +1,5 @@
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
mod maybe_trace { mod maybe_trace_private {
use crate::trace::Trace; use crate::trace::Trace;
pub trait MaybeTrace: Trace {} pub trait MaybeTrace: Trace {}
@ -7,9 +7,9 @@ mod maybe_trace {
} }
#[cfg(not(feature = "ui_debug"))] #[cfg(not(feature = "ui_debug"))]
mod maybe_trace { mod maybe_trace_private {
pub trait MaybeTrace {} pub trait MaybeTrace {}
impl<T> MaybeTrace for T {} 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> { impl<F: FnMut(&str)> ListTracer for JsonTracer<F> {
fn child(&mut self, value: &dyn Trace) { 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) { fn int(&mut self, i: i64) {
@ -196,7 +196,6 @@ pub trait Trace {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
extern crate serde_json;
use serde_json::Value; use serde_json::Value;
use super::*; use super::*;

View File

@ -215,7 +215,7 @@ where
U: crate::trace::Trace, U: crate::trace::Trace,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { 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.0);
l.child(&self.1); l.child(&self.1);
}); });

View File

@ -6,6 +6,7 @@ use crate::{
display, display,
display::{Color, Font}, display::{Color, Font},
geometry::Rect, geometry::Rect,
util::animation_disabled,
}, },
}; };
@ -54,6 +55,11 @@ where
} }
pub fn start(&mut self, ctx: &mut EventCtx, now: Instant) { 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 { if let State::Initial = self.state {
let text_width = self.font.text_width(self.text.as_ref()); let text_width = self.font.text_width(self.text.as_ref());
let max_offset = self.area.width() - text_width; 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> { 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(); let now = Instant::now();
if let Event::Timer(token) = event { if let Event::Timer(token) = event {

View File

@ -29,7 +29,9 @@ pub enum PageMsg<T, U> {
} }
pub trait Paginate { pub trait Paginate {
/// How many pages of content are there in total?
fn page_count(&mut self) -> usize; fn page_count(&mut self) -> usize;
/// Navigate to the given page.
fn change_page(&mut self, active_page: usize); fn change_page(&mut self, active_page: usize);
} }

View File

@ -155,7 +155,7 @@ where
use core::cell::Cell; use core::cell::Cell;
let fit: Cell<Option<LayoutFit>> = Cell::new(None); let fit: Cell<Option<LayoutFit>> = Cell::new(None);
t.component("FormattedText"); t.component("FormattedText");
t.in_list("text", &mut |l| { t.in_list("text", &|l| {
let result = self.layout_content(&mut TraceSink(l)); let result = self.layout_content(&mut TraceSink(l));
fit.set(Some(result)); fit.set(Some(result));
}); });

View File

@ -1,7 +1,6 @@
use super::iter::GlyphMetrics;
use crate::ui::{ use crate::ui::{
display, display,
display::{toif::Icon, Color, Font}, display::{toif::Icon, Color, Font, GlyphMetrics},
geometry::{Alignment, Dimensions, Offset, Point, Rect, BOTTOM_LEFT}, geometry::{Alignment, Dimensions, Offset, Point, Rect, BOTTOM_LEFT},
}; };
@ -55,9 +54,9 @@ pub struct TextLayout {
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct TextStyle { pub struct TextStyle {
/// Text font ID. Can be overridden by `Op::Font`. /// Text font ID.
pub text_font: Font, pub text_font: Font,
/// Text color. Can be overridden by `Op::Color`. /// Text color.
pub text_color: Color, pub text_color: Color,
/// Background color. /// Background color.
pub background_color: Color, pub background_color: Color,
@ -161,16 +160,19 @@ impl TextLayout {
self self
} }
/// Baseline `Point` where we are starting to draw the text.
pub fn initial_cursor(&self) -> Point { pub fn initial_cursor(&self) -> Point {
let font = &self.style.text_font; let font = &self.style.text_font;
self.bounds.top_left() self.bounds.top_left()
+ Offset::y(font.text_max_height() - font.text_baseline() + self.padding_top) + 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 { pub fn fit_text(&self, text: &str) -> LayoutFit {
self.layout_text(text, &mut self.initial_cursor(), &mut TextNoOp) 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) { pub fn render_text(&self, text: &str) {
self.layout_text(text, &mut self.initial_cursor(), &mut TextRenderer); 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( pub fn layout_text(
&self, &self,
text: &str, text: &str,
@ -225,11 +230,10 @@ impl TextLayout {
sink: &mut dyn LayoutSink, sink: &mut dyn LayoutSink,
) -> LayoutFit { ) -> LayoutFit {
let init_cursor = *cursor; let init_cursor = *cursor;
let bottom = (self.bounds.y1 - self.padding_bottom).max(self.bounds.y0);
let mut remaining_text = text; let mut remaining_text = text;
// Check if bounding box is high enough for at least one line. // 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(); sink.out_of_bounds();
return LayoutFit::OutOfBounds { return LayoutFit::OutOfBounds {
processed_chars: 0, processed_chars: 0,
@ -244,6 +248,7 @@ impl TextLayout {
) && self.continues_from_prev_page ) && self.continues_from_prev_page
{ {
sink.prev_page_ellipsis(*cursor, self); sink.prev_page_ellipsis(*cursor, self);
// Move the cursor to the right, always the same distance
cursor.x += self.style.prev_page_ellipsis_width(); cursor.x += self.style.prev_page_ellipsis_width();
} }
@ -271,7 +276,12 @@ impl TextLayout {
}; };
// Report the span at the cursor position. // 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. // Continue with the rest of the remaining_text.
remaining_text = &remaining_text[span.length + span.skip_next_chars..]; remaining_text = &remaining_text[span.length + span.skip_next_chars..];
@ -287,7 +297,8 @@ impl TextLayout {
sink.hyphen(*cursor, self); sink.hyphen(*cursor, self);
} }
// Check the amount of vertical space we have left. // 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() { if !remaining_text.is_empty() {
// Append ellipsis to indicate more content is available, but only if we // Append ellipsis to indicate more content is available, but only if we
// haven't already appended a hyphen. // 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 { fn layout_height(&self, init_cursor: Point, end_cursor: Point) -> i16 {
self.padding_top self.padding_top
+ self.style.text_font.text_height() + self.style.text_font.text_height()
@ -336,7 +348,8 @@ impl TextLayout {
+ self.padding_bottom + 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) (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 { pub enum LayoutFit {
/// Entire content fits. Vertical size is returned in `height`. /// Entire content fits. Vertical size is returned in `height`.
Fitting { processed_chars: usize, height: i16 }, Fitting { processed_chars: usize, height: i16 },
@ -359,6 +375,7 @@ pub enum LayoutFit {
} }
impl LayoutFit { impl LayoutFit {
/// How high is the processed/fitted content.
pub fn height(&self) -> i16 { pub fn height(&self) -> i16 {
match self { match self {
LayoutFit::Fitting { height, .. } => *height, LayoutFit::Fitting { height, .. } => *height,
@ -368,24 +385,36 @@ impl LayoutFit {
} }
/// Visitor for text segment operations. /// Visitor for text segment operations.
/// Defines responses for certain kind of events encountered
/// when processing the content.
pub trait LayoutSink { pub trait LayoutSink {
/// Text should be processed.
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {} fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {}
/// Hyphen at the end of line.
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {} fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
/// Ellipsis at the end of the page.
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {} 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) {} fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
/// Line break - a newline.
fn line_break(&mut self, _cursor: Point) {} fn line_break(&mut self, _cursor: Point) {}
/// Content cannot fit on the screen.
fn out_of_bounds(&mut self) {} 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; pub struct TextNoOp;
impl LayoutSink for TextNoOp {} impl LayoutSink for TextNoOp {}
/// `LayoutSink` for rendering the content.
pub struct TextRenderer; pub struct TextRenderer;
impl LayoutSink for TextRenderer { impl LayoutSink for TextRenderer {
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) { fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) {
display::text( display::text_left(
cursor, cursor,
text, text,
layout.style.text_font, layout.style.text_font,
@ -395,7 +424,7 @@ impl LayoutSink for TextRenderer {
} }
fn hyphen(&mut self, cursor: Point, layout: &TextLayout) { fn hyphen(&mut self, cursor: Point, layout: &TextLayout) {
display::text( display::text_left(
cursor, cursor,
"-", "-",
layout.style.text_font, layout.style.text_font,
@ -405,15 +434,16 @@ impl LayoutSink for TextRenderer {
} }
fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) { 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( icon.draw(
cursor, bottom_left,
BOTTOM_LEFT, BOTTOM_LEFT,
layout.style.ellipsis_color, layout.style.ellipsis_color,
layout.style.background_color, layout.style.background_color,
); );
} else { } else {
display::text( display::text_left(
cursor, cursor,
ELLIPSIS, ELLIPSIS,
layout.style.text_font, layout.style.text_font,
@ -424,7 +454,7 @@ impl LayoutSink for TextRenderer {
} }
fn prev_page_ellipsis(&mut self, cursor: Point, layout: &TextLayout) { 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( icon.draw(
cursor, cursor,
BOTTOM_LEFT, BOTTOM_LEFT,
@ -432,7 +462,7 @@ impl LayoutSink for TextRenderer {
layout.style.background_color, layout.style.background_color,
); );
} else { } else {
display::text( display::text_left(
cursor, cursor,
ELLIPSIS, ELLIPSIS,
layout.style.text_font, layout.style.text_font,
@ -454,23 +484,23 @@ pub mod trace {
impl LayoutSink for TraceSink<'_> { impl LayoutSink for TraceSink<'_> {
fn text(&mut self, _cursor: Point, _layout: &TextLayout, text: &str) { 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) { fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {
self.0.string(&"-"); self.0.string("-");
} }
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) { 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) { 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) { 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)] #[derive(Debug, PartialEq, Eq)]
struct Span { struct Span {
/// How many characters from the input text this span is laying out. /// How many characters from the input text this span is laying out.
@ -603,7 +635,7 @@ impl Span {
} }
found_any_whitespace = true; found_any_whitespace = true;
} else if span_width + char_width > max_width { } else if span_width + char_width > max_width {
// Return the last breakpoint. // Cannot fit on this line. Return the last breakpoint.
return line; return line;
} else { } else {
let have_space_for_break = let have_space_for_break =
@ -625,7 +657,7 @@ impl Span {
span_width += char_width; span_width += char_width;
} }
// The whole text is fitting. // The whole text is fitting on the current line.
Self { Self {
length: text.len(), length: text.len(),
advance: Offset::x(span_width), advance: Offset::x(span_width),

View File

@ -239,13 +239,13 @@ pub mod trace {
impl<T: ParagraphSource> crate::trace::Trace for Paragraphs<T> { impl<T: ParagraphSource> crate::trace::Trace for Paragraphs<T> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.string("component", "Paragraphs"); t.string("component", "Paragraphs");
t.in_list("paragraphs", &mut |par_list| { t.in_list("paragraphs", &|par_list| {
Self::foreach_visible( Self::foreach_visible(
&self.source, &self.source,
&self.visible, &self.visible,
self.offset, self.offset,
&mut |layout, content| { &mut |layout, content| {
par_list.in_list(&mut |par| { par_list.in_list(&|par| {
layout.layout_text( layout.layout_text(
content, content,
&mut layout.initial_cursor(), &mut layout.initial_cursor(),

View File

@ -1,6 +1,6 @@
use crate::ui::{ use crate::ui::{
constant::{screen, LOADER_OUTER}, 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}, geometry::{Offset, Point, Rect, CENTER},
}; };
use core::f32::consts::SQRT_2; 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) { fn star_medium(center: Point, fg: Color, bg: Color) {
let r = Rect::from_center_and_size(center, Offset::uniform(SIZE_MEDIUM)); 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) { fn star_large(center: Point, fg: Color, bg: Color) {

View File

@ -15,6 +15,9 @@ use crate::trezorhal::{
}, },
uzlib::UZLIB_WINDOW_SIZE, uzlib::UZLIB_WINDOW_SIZE,
}; };
#[cfg(feature = "dma2d")]
use crate::ui::component::image::Image;
#[cfg(not(feature = "dma2d"))] #[cfg(not(feature = "dma2d"))]
use crate::ui::geometry::TOP_LEFT; use crate::ui::geometry::TOP_LEFT;
@ -23,12 +26,11 @@ use crate::{
trezorhal::{buffers, display, time, uzlib::UzlibContext}, trezorhal::{buffers, display, time, uzlib::UzlibContext},
ui::lerp::Lerp, ui::lerp::Lerp,
}; };
use core::slice;
#[cfg(feature = "dma2d")] // Reexports
use crate::ui::component::image::Image;
pub use crate::ui::display::toif::Icon; 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 use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN};
pub fn backlight() -> u16 { pub fn backlight() -> u16 {
@ -58,6 +60,7 @@ pub fn fade_backlight_duration(target: u16, duration_ms: u32) {
set_backlight(target as u16); set_backlight(target as u16);
} }
/// Fill a whole rectangle with a specific color.
pub fn rect_fill(r: Rect, fg_color: Color) { pub fn rect_fill(r: Rect, fg_color: Color) {
display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into()); 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()); 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) { 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)); if radius == 1 {
display::bar_radius( rect_fill_rounded1(r, fg_color, bg_color);
r.x0, } else {
r.y0, assert!([2, 4, 8, 16].iter().any(|allowed| radius == *allowed));
r.width(), display::bar_radius(
r.height(), r.x0,
fg_color.into(), r.y0,
bg_color.into(), r.width(),
radius, r.height(),
); fg_color.into(),
bg_color.into(),
radius,
);
}
} }
// Used on T1 only. /// Filling a rectangle with a rounding of 1 pixel - removing the corners.
pub fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) { 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()); rect_fill(r, fg_color);
let corners = [ rect_fill_corners(r, bg_color);
r.top_left(), }
r.top_right() - Offset::x(1),
r.bottom_right() - Offset::uniform(1), /// Creating a rectangular outline with a given radius/rounding.
r.bottom_left() - Offset::y(1), 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
for p in corners.iter() { // to create the outline.
display::bar(p.x, p.y, 1, 1, bg_color.into()); 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, text,
font, font,
max_height: font.max_height(), 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( display::text(
baseline.x, baseline.x,
baseline.y, 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) { pub fn text_center(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
let w = font.text_width(text); let w = font.text_width(text);
display::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) { pub fn text_right(baseline: Point, text: &str, font: Font, fg_color: Color, bg_color: Color) {
let w = font.text_width(text); let w = font.text_width(text);
display::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 /// A rectangle in 2D space defined by the top-left point `x0`,`y0` and the
/// bottom-right point `x1`,`y1`. /// 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)] #[derive(Copy, Clone, PartialEq, Eq)]
pub struct Rect { pub struct Rect {
pub x0: i16, pub x0: i16,
@ -304,10 +306,12 @@ impl Rect {
self.bottom_left().center(self.bottom_right()) self.bottom_left().center(self.bottom_right())
} }
/// Whether a `Point` is inside the `Rect`.
pub const fn contains(&self, point: Point) -> bool { pub const fn contains(&self, point: Point) -> bool {
point.x >= self.x0 && point.x < self.x1 && point.y >= self.y0 && point.y < self.y1 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 { pub const fn union(&self, other: Self) -> Self {
Self { Self {
x0: min(self.x0, other.x0), 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 { pub const fn inset(&self, insets: Insets) -> Self {
Self { Self {
x0: self.x0 + insets.left, x0: self.x0 + insets.left,
@ -335,24 +341,12 @@ impl Rect {
} }
} }
pub const fn cut_from_left(&self, width: i16) -> Self { /// Move all the sides closer to the center by the same distance.
Self { pub const fn shrink(&self, size: i16) -> Self {
x0: self.x0, self.inset(Insets::uniform(size))
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,
}
} }
/// Split `Rect` into top and bottom, given the top one's `height`.
pub const fn split_top(self, height: i16) -> (Self, Self) { pub const fn split_top(self, height: i16) -> (Self, Self) {
let height = clamp(height, 0, self.height()); let height = clamp(height, 0, self.height());
@ -367,10 +361,12 @@ impl Rect {
(top, bottom) (top, bottom)
} }
/// Split `Rect` into top and bottom, given the bottom one's `height`.
pub const fn split_bottom(self, height: i16) -> (Self, Self) { pub const fn split_bottom(self, height: i16) -> (Self, Self) {
self.split_top(self.height() - height) 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) { pub const fn split_left(self, width: i16) -> (Self, Self) {
let width = clamp(width, 0, self.width()); let width = clamp(width, 0, self.width());
@ -385,6 +381,7 @@ impl Rect {
(left, right) (left, right)
} }
/// Split `Rect` into left and right, given the right one's `width`.
pub const fn split_right(self, width: i16) -> (Self, Self) { pub const fn split_right(self, width: i16) -> (Self, Self) {
self.split_left(self.width() - width) 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 { pub const fn translate(&self, offset: Offset) -> Self {
Self { Self {
x0: self.x0 + offset.x, x0: self.x0 + offset.x,
@ -414,6 +412,16 @@ impl Rect {
y1: self.y1 + offset.y, 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)] #[derive(Copy, Clone, PartialEq, Eq)]
@ -538,7 +546,7 @@ impl Grid {
let cell_height = (self.area.height() - spacing_height) / nrows; let cell_height = (self.area.height() - spacing_height) / nrows;
// Not every area can be fully covered by equal-sized cells and spaces, there // 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. // the leftmost cells.
let leftover_width = (self.area.width() - spacing_width) % ncols; let leftover_width = (self.area.width() - spacing_width) % ncols;
let leftover_height = (self.area.height() - spacing_height) % nrows; let leftover_height = (self.area.height() - spacing_height) % nrows;

View File

@ -19,7 +19,7 @@ use crate::ui::{
text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt}, text::paragraphs::{Paragraph, ParagraphVecShort, Paragraphs, VecExt},
Event, EventCtx, Event, EventCtx,
}, },
constant::{screen, BACKLIGHT_NORMAL, WIDTH}, constant::{screen, WIDTH},
display::{fade_backlight_duration, Color, Icon, TextOverlay}, display::{fade_backlight_duration, Color, Icon, TextOverlay},
event::ButtonEvent, event::ButtonEvent,
geometry::{LinearPlacement, Offset, Rect, CENTER}, geometry::{LinearPlacement, Offset, Rect, CENTER},
@ -31,7 +31,7 @@ use crate::ui::{
theme::{bld_button_cancel, bld_button_default, BLD_BG, BLD_FG}, theme::{bld_button_cancel, bld_button_default, BLD_BG, BLD_FG},
}, },
component::{Button, ButtonPos, ResultScreen}, 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}, util::{from_c_array, from_c_str},
}; };

View File

@ -132,12 +132,12 @@ where
ButtonContent::Text(text) => { ButtonContent::Text(text) => {
let background_color = style.text_color.negate(); let background_color = style.text_color.negate();
if style.border_horiz { 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 { } else {
display::rect_fill(self.area, background_color) display::rect_fill(self.area, background_color)
} }
display::text( display::text_left(
self.baseline, self.baseline,
text.as_ref(), text.as_ref(),
style.font, style.font,

View File

@ -83,7 +83,11 @@ where
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Dialog"); t.component("Dialog");
t.child("content", &self.content); t.child("content", &self.content);
self.left_btn.as_ref().map(|b| t.child("left", b)); if let Some(b) = self.left_btn.as_ref() {
self.right_btn.as_ref().map(|b| t.child("right", b)); 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) { fn paint(&mut self) {
display::text( display::text_left(
self.area.bottom_left() - Offset::y(2), self.area.bottom_left() - Offset::y(2),
self.title.as_ref(), self.title.as_ref(),
Font::BOLD, 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) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ResultPopup"); t.component("ResultPopup");
t.child("text", &self.text); t.child("text", &self.text);
self.button.as_ref().map(|b| t.child("button", b)); if let Some(b) = self.button.as_ref() {
self.headline.as_ref().map(|h| t.child("headline", h)); t.child("button", b)
}
if let Some(h) = self.headline.as_ref() {
t.child("headline", h)
}
t.child("result_anim", &self.result_anim); t.child("result_anim", &self.result_anim);
} }
} }

View File

@ -144,98 +144,3 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Confirm text.""" /// """Confirm text."""
Qstr::MP_QSTR_confirm_text => obj_fn_kw!(0, new_confirm_text).as_obj(), 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}; use super::component::{ButtonStyle, ButtonStyleSheet};
// Typical backlight values.
pub const BACKLIGHT_NORMAL: u16 = 150;
// Color palette. // Color palette.
pub const WHITE: Color = Color::rgb(255, 255, 255); pub const WHITE: Color = Color::rgb(255, 255, 255);
pub const BLACK: Color = Color::rgb(0, 0, 0); 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() let start_of_baseline = self.area.center()
+ Offset::new(-width / 2, height / 2) + Offset::new(-width / 2, height / 2)
+ Offset::y(Self::BASELINE_OFFSET); + Offset::y(Self::BASELINE_OFFSET);
display::text( display::text_left(
start_of_baseline, start_of_baseline,
text, text,
style.font, style.font,
@ -575,7 +575,7 @@ impl IconText {
} }
if use_text { if use_text {
display::text( display::text_left(
text_pos, text_pos,
self.text, self.text,
style.font, style.font,

View File

@ -191,8 +191,13 @@ where
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Frame"); t.component("Frame");
t.child("title", &self.title); t.child("title", &self.title);
self.subtitle.as_ref().map(|s| t.child("subtitle", s)); t.child("content", &self.content);
self.button.as_ref().map(|b| t.child("button", b)); 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::{ use crate::{
trezorhal::bip39, trezorhal::bip39,
ui::{ ui::{
component::{Component, Event, EventCtx}, component::{text::common::TextBox, Component, Event, EventCtx},
display, display,
display::toif::Icon, display::toif::Icon,
geometry::{Offset, Rect, CENTER}, geometry::{Offset, Rect, CENTER},
model_tt::{ model_tt::{
component::{ component::{
keyboard::{ keyboard::{
common::{paint_pending_marker, MultiTapKeyboard, TextBox}, common::{paint_pending_marker, MultiTapKeyboard},
mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT}, mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT},
}, },
Button, ButtonContent, ButtonMsg, 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 // Content starts in the left-center point, offset by 16px to the right and 8px
// to the bottom. // to the bottom.
let text_baseline = area.top_left().center(area.bottom_left()) + Offset::new(16, 8); let text_baseline = area.top_left().center(area.bottom_left()) + Offset::new(16, 8);
display::text( display::text_left(
text_baseline, text_baseline,
text, text,
style.font, style.font,
@ -121,7 +121,7 @@ impl Component for Bip39Input {
// Paint the rest of the suggested dictionary word. // Paint the rest of the suggested dictionary word.
if let Some(word) = self.suggested_word.and_then(|w| w.get(text.len()..)) { if let Some(word) = self.suggested_word.and_then(|w| w.get(text.len()..)) {
let word_baseline = text_baseline + Offset::new(width, 0); let word_baseline = text_baseline + Offset::new(width, 0);
display::text( display::text_left(
word_baseline, word_baseline,
word, word,
style.font, style.font,

View File

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

View File

@ -468,5 +468,16 @@ where
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PinKeyboard"); 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::{ use crate::{
trezorhal::slip39, trezorhal::slip39,
ui::{ ui::{
component::{Component, Event, EventCtx}, component::{
text::common::{TextBox, TextEdit},
Component, Event, EventCtx,
},
display, display,
display::toif::Icon, display::toif::Icon,
geometry::{Offset, Rect, CENTER}, geometry::{Offset, Rect, CENTER},
model_tt::{ model_tt::{
component::{ component::{
keyboard::{ keyboard::{
common::{paint_pending_marker, MultiTapKeyboard, TextBox, TextEdit}, common::{paint_pending_marker, MultiTapKeyboard},
mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT}, mnemonic::{MnemonicInput, MnemonicInputMsg, MNEMONIC_KEY_COUNT},
}, },
Button, ButtonContent, ButtonMsg, Button, ButtonContent, ButtonMsg,
@ -156,7 +159,7 @@ impl Component for Slip39Input {
.assert_if_debugging_ui("Text buffer is too small"); .assert_if_debugging_ui("Text buffer is too small");
} }
} }
display::text( display::text_left(
text_baseline, text_baseline,
text.as_str(), text.as_str(),
style.font, style.font,

View File

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

View File

@ -9,8 +9,6 @@ pub const LOADER_OUTER: i16 = 60;
pub const LOADER_INNER: i16 = 42; pub const LOADER_INNER: i16 = 42;
pub const LOADER_ICON_MAX_SIZE: i16 = 64; pub const LOADER_ICON_MAX_SIZE: i16 = 64;
pub const BACKLIGHT_NORMAL: i32 = 150;
pub const fn size() -> Offset { pub const fn size() -> Offset {
Offset::new(WIDTH, HEIGHT) 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 { extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title).unwrap().try_into().unwrap(); let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = kwargs let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
.get(Qstr::MP_QSTR_description) let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?;
.unwrap() let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
.try_into() let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?;
.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 paragraphs = Paragraphs::new([ let paragraphs = Paragraphs::new([
Paragraph::new(&theme::TEXT_DEMIBOLD, title).centered(), 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 { 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 block = move |_args: &[Obj], kwargs: &Map| {
let dry_run: bool = kwargs let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?;
.get(Qstr::MP_QSTR_dry_run)
.unwrap()
.try_into()
.unwrap();
let title = if dry_run { let title = if dry_run {
"SEED CHECK" "SEED CHECK"
} else { } else {
@ -1554,7 +1538,7 @@ extern "C" fn draw_welcome_screen() -> Obj {
screen.place(constant::screen()); screen.place(constant::screen());
display::sync(); display::sync();
screen.paint(); screen.paint();
display::set_backlight(150); // BACKLIGHT_NORMAL display::set_backlight(theme::BACKLIGHT_NORMAL);
Obj::const_none() Obj::const_none()
} }
@ -1575,8 +1559,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// """Disable animations, debug builds only.""" /// """Disable animations, debug builds only."""
Qstr::MP_QSTR_disable_animation => obj_fn_1!(upy_disable_animation).as_obj(), 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): /// def jpeg_info(data: bytes) -> tuple[int, int, int]:
/// """Get JPEG image dimensions.""" /// """Get JPEG image dimensions (width: int, height: int, mcu_height: int)."""
Qstr::MP_QSTR_jpeg_info => obj_fn_1!(upy_jpeg_info).as_obj(), Qstr::MP_QSTR_jpeg_info => obj_fn_1!(upy_jpeg_info).as_obj(),
/// def jpeg_test(data: bytes) -> bool: /// def jpeg_test(data: bytes) -> bool:
@ -1662,7 +1646,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_spending_details( /// def show_spending_details(
/// *, /// *,
/// account: str, /// account: str,
/// fee_rate: str | None = None, /// fee_rate: str | None,
/// ) -> object: /// ) -> object:
/// """Show metadata when for outgoing transaction.""" /// """Show metadata when for outgoing transaction."""
Qstr::MP_QSTR_show_spending_details => obj_fn_kw!(0, new_show_spending_details).as_obj(), 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, /// title: str,
/// value: str, /// value: str,
/// description: str | None = None, /// description: str | None,
/// subtitle: str | None = None, /// subtitle: str | None,
/// verb: str | None = None, /// verb: str | None = None,
/// verb_cancel: str | None = None, /// verb_cancel: str | None = None,
/// info_button: bool = False, /// info_button: bool = False,
@ -1773,8 +1757,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// def show_simple( /// def show_simple(
/// *, /// *,
/// title: str | None, /// title: str | None,
/// description: str, /// description: str = "",
/// button: str | None = None, /// button: str = "",
/// ) -> object: /// ) -> object:
/// """Simple dialog with text and one button.""" /// """Simple dialog with text and one button."""
Qstr::MP_QSTR_show_simple => obj_fn_kw!(0, new_show_simple).as_obj(), 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, /// prompt: str,
/// max_len: int, /// max_len: int,
/// ) -> str | object: /// ) -> str | object:
/// """Passphrase input keyboard.""" /// """Passphrase input keyboard."""
Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(), Qstr::MP_QSTR_request_passphrase => obj_fn_kw!(0, new_request_passphrase).as_obj(),
/// def request_bip39( /// def request_bip39(
/// *, /// *,
/// prompt: str, /// prompt: str,
/// ) -> str: /// ) -> str:
/// """BIP39 word input keyboard.""" /// """BIP39 word input keyboard."""
Qstr::MP_QSTR_request_bip39 => obj_fn_kw!(0, new_request_bip39).as_obj(), Qstr::MP_QSTR_request_bip39 => obj_fn_kw!(0, new_request_bip39).as_obj(),
/// def request_slip39( /// def request_slip39(
/// *, /// *,
/// prompt: str, /// prompt: str,
/// ) -> str: /// ) -> str:
/// """SLIP39 word input keyboard.""" /// """SLIP39 word input keyboard."""
Qstr::MP_QSTR_request_slip39 => obj_fn_kw!(0, new_request_slip39).as_obj(), Qstr::MP_QSTR_request_slip39 => obj_fn_kw!(0, new_request_slip39).as_obj(),
/// def select_word( /// def select_word(
@ -1846,7 +1830,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// description: str, /// description: str,
/// words: Iterable[str], /// words: Iterable[str],
/// ) -> int: /// ) -> 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`.""" /// 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(), 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, /// title: str,
/// pages: Iterable[str], /// pages: Iterable[str],
/// ) -> object: /// ) -> 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(), Qstr::MP_QSTR_show_share_words => obj_fn_kw!(0, new_show_share_words).as_obj(),
/// def request_number( /// def request_number(
@ -1866,7 +1850,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// max_count: int, /// max_count: int,
/// description: Callable[[int], str] | None = None, /// description: Callable[[int], str] | None = None,
/// ) -> object: /// ) -> 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(), Qstr::MP_QSTR_request_number => obj_fn_kw!(0, new_request_number).as_obj(),
/// def show_checklist( /// def show_checklist(
@ -1876,8 +1860,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// active: int, /// active: int,
/// button: str, /// button: str,
/// ) -> object: /// ) -> object:
/// """Checklist of backup steps. Active index is highlighted, previous items have check /// """Checklist of backup steps. Active index is highlighted, previous items have check
/// mark nex to them.""" /// mark next to them."""
Qstr::MP_QSTR_show_checklist => obj_fn_kw!(0, new_show_checklist).as_obj(), Qstr::MP_QSTR_show_checklist => obj_fn_kw!(0, new_show_checklist).as_obj(),
/// def confirm_recovery( /// def confirm_recovery(
@ -1886,41 +1870,41 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// description: str, /// description: str,
/// button: str, /// button: str,
/// dry_run: bool, /// dry_run: bool,
/// info_button: bool, /// info_button: bool = False,
/// ) -> object: /// ) -> object:
/// """Device recovery homescreen.""" /// """Device recovery homescreen."""
Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(), Qstr::MP_QSTR_confirm_recovery => obj_fn_kw!(0, new_confirm_recovery).as_obj(),
/// def select_word_count( /// def select_word_count(
/// *, /// *,
/// dry_run: bool, /// dry_run: bool,
/// ) -> int | CANCELLED: /// ) -> int | str: # TT returns int
/// """Select mnemonic word count from (12, 18, 20, 24, 33).""" /// """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(), Qstr::MP_QSTR_select_word_count => obj_fn_kw!(0, new_select_word_count).as_obj(),
/// def show_group_share_success( /// def show_group_share_success(
/// *, /// *,
/// lines: Iterable[str] /// lines: Iterable[str]
/// ) -> int: /// ) -> 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(), Qstr::MP_QSTR_show_group_share_success => obj_fn_kw!(0, new_show_group_share_success).as_obj(),
/// def show_remaining_shares( /// def show_remaining_shares(
/// *, /// *,
/// pages: Iterable[tuple[str, str]], /// pages: Iterable[tuple[str, str]],
/// ) -> int: /// ) -> 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(), Qstr::MP_QSTR_show_remaining_shares => obj_fn_kw!(0, new_show_remaining_shares).as_obj(),
/// def show_progress( /// def show_progress(
/// *, /// *,
/// title: str, /// title: str,
/// indeterminate: bool = False, /// indeterminate: bool = False,
/// description: str | None = None, /// description: str = "",
/// ) -> object: /// ) -> 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 /// 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(), Qstr::MP_QSTR_show_progress => obj_fn_kw!(0, new_show_progress).as_obj(),
/// def show_progress_coinjoin( /// def show_progress_coinjoin(
@ -1930,7 +1914,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// time_ms: int = 0, /// time_ms: int = 0,
/// skip_first_paint: bool = False, /// skip_first_paint: bool = False,
/// ) -> object: /// ) -> 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.""" /// time_ms timeout is passed."""
Qstr::MP_QSTR_show_progress_coinjoin => obj_fn_kw!(0, new_show_progress_coinjoin).as_obj(), 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)] #[cfg(test)]
mod tests { mod tests {
extern crate serde_json; use serde_json;
use crate::{ use crate::{
trace::tests::trace, trace::tests::trace,