refactor(core/rust) use TString in Label and Button

[no changelog]
pull/3649/head
tychovrahe 2 months ago
parent a3e0b79ebb
commit 578dc94201

@ -78,7 +78,7 @@ pub enum TString<'a> {
Str(&'a str), Str(&'a str),
} }
impl TString<'_> { impl<'a> TString<'a> {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.map(|s| s.len()) self.map(|s| s.len())
} }
@ -87,10 +87,10 @@ impl TString<'_> {
self.len() == 0 self.len() == 0
} }
pub fn map<F, T>(&self, fun: F) -> T pub fn map<F, T>(&'a self, fun: F) -> T
where where
F: for<'a> FnOnce(&'a str) -> T, F: FnOnce(&'a str) -> T,
T: 'static, T: 'a,
{ {
match self { match self {
#[cfg(feature = "micropython")] #[cfg(feature = "micropython")]
@ -147,6 +147,12 @@ impl<'a> From<&'a str> for TString<'a> {
} }
} }
impl<'a> From<&'a TString<'a>> for &'a str {
fn from(s: &'a TString<'a>) -> &'a str {
s.map(|s| s)
}
}
#[cfg(feature = "translations")] #[cfg(feature = "translations")]
impl From<TR> for TString<'static> { impl From<TR> for TString<'static> {
fn from(tr: TR) -> Self { fn from(tr: TR) -> Self {

@ -7,6 +7,7 @@ mod public_keys;
mod translated_string; mod translated_string;
pub use blob::MAX_HEADER_LEN; pub use blob::MAX_HEADER_LEN;
pub use flash::{get, init};
pub use translated_string::TranslatedString as TR; pub use translated_string::TranslatedString as TR;
pub const DEFAULT_LANGUAGE: &str = "en-US"; pub const DEFAULT_LANGUAGE: &str = "en-US";

@ -4,16 +4,15 @@ use super::blob::Translations;
pub use super::generated::translated_string::TranslatedString; pub use super::generated::translated_string::TranslatedString;
impl TranslatedString { impl TranslatedString {
pub(super) fn translate<'a>(self, source: Option<&'a Translations>) -> &'a str { pub fn translate<'a>(self, source: Option<&'a Translations>) -> &'a str {
source source
.and_then(|s| s.translation(self as _)) .and_then(|s| s.translation(self as _))
.unwrap_or(self.untranslated()) .unwrap_or(self.untranslated())
} }
pub fn map_translated<F, T>(self, fun: F) -> T pub fn map_translated<'a, F, T>(self, fun: F) -> T
where where
F: for<'a> FnOnce(&'a str) -> T, F: FnOnce(&'a str) -> T,
T: 'static,
{ {
// SAFETY: The bound on F _somehow_ ensures that the reference cannot escape // SAFETY: The bound on F _somehow_ ensures that the reference cannot escape
// the closure. (I don't understand how, but it does), see soundness test below. // the closure. (I don't understand how, but it does), see soundness test below.

@ -1,22 +1,22 @@
use crate::ui::{ use crate::{
component::{Component, Event, EventCtx, Never}, strutil::TString,
display::Font, ui::{
geometry::{Alignment, Insets, Offset, Point, Rect}, component::{Component, Event, EventCtx, Never},
display::Font,
geometry::{Alignment, Insets, Offset, Point, Rect},
},
}; };
use super::{text::TextStyle, TextLayout}; use super::{text::TextStyle, TextLayout};
pub struct Label<T> { pub struct Label<'a> {
text: T, text: TString<'a>,
layout: TextLayout, layout: TextLayout,
vertical: Alignment, vertical: Alignment,
} }
impl<T> Label<T> impl<'a> Label<'a> {
where pub fn new(text: TString<'a>, align: Alignment, style: TextStyle) -> Self {
T: AsRef<str>,
{
pub fn new(text: T, align: Alignment, style: TextStyle) -> Self {
Self { Self {
text, text,
layout: TextLayout::new(style).with_align(align), layout: TextLayout::new(style).with_align(align),
@ -24,15 +24,15 @@ where
} }
} }
pub fn left_aligned(text: T, style: TextStyle) -> Self { pub fn left_aligned(text: TString<'a>, style: TextStyle) -> Self {
Self::new(text, Alignment::Start, style) Self::new(text, Alignment::Start, style)
} }
pub fn right_aligned(text: T, style: TextStyle) -> Self { pub fn right_aligned(text: TString<'a>, style: TextStyle) -> Self {
Self::new(text, Alignment::End, style) Self::new(text, Alignment::End, style)
} }
pub fn centered(text: T, style: TextStyle) -> Self { pub fn centered(text: TString<'a>, style: TextStyle) -> Self {
Self::new(text, Alignment::Center, style) Self::new(text, Alignment::Center, style)
} }
@ -41,11 +41,11 @@ where
self self
} }
pub fn text(&self) -> &T { pub fn text(&self) -> &TString<'a> {
&self.text &self.text
} }
pub fn set_text(&mut self, text: T) { pub fn set_text(&mut self, text: TString<'a>) {
self.text = text; self.text = text;
} }
@ -63,14 +63,17 @@ where
pub fn max_size(&self) -> Offset { pub fn max_size(&self) -> Offset {
let font = self.font(); let font = self.font();
Offset::new(font.text_width(self.text.as_ref()), font.text_max_height()) Offset::new(
font.text_width(self.text.map(|c| c)),
font.text_max_height(),
)
} }
pub fn text_height(&self, width: i16) -> i16 { pub fn text_height(&self, width: i16) -> i16 {
let bounds = Rect::from_top_left_and_size(Point::zero(), Offset::new(width, i16::MAX)); let bounds = Rect::from_top_left_and_size(Point::zero(), Offset::new(width, i16::MAX));
self.layout self.layout
.with_bounds(bounds) .with_bounds(bounds)
.fit_text(self.text.as_ref()) .fit_text(self.text.map(|c| c))
.height() .height()
} }
@ -78,7 +81,7 @@ where
// XXX only works on single-line labels // XXX only works on single-line labels
assert!(self.layout.bounds.height() <= self.font().text_max_height()); assert!(self.layout.bounds.height() <= self.font().text_max_height());
let available_width = self.layout.bounds.width(); let available_width = self.layout.bounds.width();
let width = self.font().text_width(self.text.as_ref()); let width = self.font().text_width(self.text.map(|c| c));
let height = self.font().text_height(); let height = self.font().text_height();
let cursor = self.layout.initial_cursor(); let cursor = self.layout.initial_cursor();
let baseline = match self.alignment() { let baseline = match self.alignment() {
@ -90,17 +93,14 @@ where
} }
} }
impl<T> Component for Label<T> impl Component for Label<'_> {
where
T: AsRef<str>,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
let height = self let height = self
.layout .layout
.with_bounds(bounds) .with_bounds(bounds)
.fit_text(self.text.as_ref()) .fit_text(self.text.map(|c| c))
.height(); .height();
let diff = bounds.height() - height; let diff = bounds.height() - height;
let insets = match self.vertical { let insets = match self.vertical {
@ -117,7 +117,7 @@ where
} }
fn paint(&mut self) { fn paint(&mut self) {
self.layout.render_text(self.text.as_ref()); self.layout.render_text(self.text.map(|c| c));
} }
#[cfg(feature = "ui_bounds")] #[cfg(feature = "ui_bounds")]
@ -127,12 +127,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Label<T> impl crate::trace::Trace for Label<'_> {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Label"); t.component("Label");
t.string("text", self.text.as_ref().into()); t.string("text", self.text.map(|c| c).into());
} }
} }

@ -1,4 +1,5 @@
use crate::{ use crate::{
strutil::TString,
time::{Duration, Instant}, time::{Duration, Instant},
ui::{ ui::{
animation::Animation, animation::Animation,
@ -21,13 +22,13 @@ enum State {
PauseRight, PauseRight,
} }
pub struct Marquee<T> { pub struct Marquee {
area: Rect, area: Rect,
pause_token: Option<TimerToken>, pause_token: Option<TimerToken>,
min_offset: i16, min_offset: i16,
max_offset: i16, max_offset: i16,
state: State, state: State,
text: T, text: TString<'static>,
font: Font, font: Font,
fg: Color, fg: Color,
bg: Color, bg: Color,
@ -35,11 +36,8 @@ pub struct Marquee<T> {
pause: Duration, pause: Duration,
} }
impl<T> Marquee<T> impl Marquee {
where pub fn new(text: TString<'static>, font: Font, fg: Color, bg: Color) -> Self {
T: AsRef<str>,
{
pub fn new(text: T, font: Font, fg: Color, bg: Color) -> Self {
Self { Self {
area: Rect::zero(), area: Rect::zero(),
pause_token: None, pause_token: None,
@ -55,7 +53,7 @@ where
} }
} }
pub fn set_text(&mut self, text: T) { pub fn set_text(&mut self, text: TString<'static>) {
self.text = text; self.text = text;
} }
@ -66,7 +64,7 @@ where
} }
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.map(|t| t));
let max_offset = self.area.width() - text_width; let max_offset = self.area.width() - text_width;
self.min_offset = 0; self.min_offset = 0;
@ -124,7 +122,7 @@ where
pub fn paint_anim(&mut self, offset: i16) { pub fn paint_anim(&mut self, offset: i16) {
display::marquee( display::marquee(
self.area, self.area,
self.text.as_ref(), self.text.map(|t| t),
offset, offset,
self.font, self.font,
self.fg, self.fg,
@ -133,10 +131,7 @@ where
} }
} }
impl<T> Component for Marquee<T> impl Component for Marquee {
where
T: AsRef<str>,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -228,12 +223,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Marquee<T> impl crate::trace::Trace for Marquee {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Marquee"); t.component("Marquee");
t.string("text", self.text.as_ref().into()); t.string("text", self.text.into());
} }
} }

@ -1,7 +1,10 @@
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx, Label, Pad}, strutil::TString,
geometry::{Alignment, Alignment2D, Rect}, ui::{
layout::simplified::ReturnToC, component::{Child, Component, Event, EventCtx, Label, Pad},
geometry::{Alignment, Alignment2D, Rect},
layout::simplified::ReturnToC,
},
}; };
use super::super::{ use super::super::{
@ -29,14 +32,14 @@ impl ReturnToC for IntroMsg {
pub struct Intro<'a> { pub struct Intro<'a> {
bg: Pad, bg: Pad,
title: Child<Label<&'a str>>, title: Child<Label<'a>>,
buttons: Child<ButtonController>, buttons: Child<ButtonController>,
text: Child<Label<&'a str>>, text: Child<Label<'a>>,
warn: Option<Child<Label<&'a str>>>, warn: Option<Child<Label<'a>>>,
} }
impl<'a> Intro<'a> { impl<'a> Intro<'a> {
pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self { pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
Self { Self {
bg: Pad::with_background(BLD_BG).with_clear(), bg: Pad::with_background(BLD_BG).with_clear(),
title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()), title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()),
@ -46,7 +49,7 @@ impl<'a> Intro<'a> {
))), ))),
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()), text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
warn: (!fw_ok).then_some(Child::new( warn: (!fw_ok).then_some(Child::new(
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_NORMAL) Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_NORMAL)
.vertically_centered(), .vertically_centered(),
)), )),
} }

@ -77,47 +77,71 @@ extern "C" fn screen_install_confirm(
"DOWNGRADE FW" "DOWNGRADE FW"
}; };
let message = Label::left_aligned(version_str.as_str(), TEXT_NORMAL).vertically_centered(); let message =
Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL).vertically_centered();
let fingerprint = Label::left_aligned( let fingerprint = Label::left_aligned(
fingerprint_str, fingerprint_str.into(),
TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen), TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
) )
.vertically_centered(); .vertically_centered();
let alert = let alert = (!should_keep_seed).then_some(Label::left_aligned(
(!should_keep_seed).then_some(Label::left_aligned("Seed will be erased!", TEXT_NORMAL)); "Seed will be erased!".into(),
TEXT_NORMAL,
));
let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false) let mut frame = Confirm::new(
.with_info_screen("FW FINGERPRINT", fingerprint); BLD_BG,
title_str.into(),
message,
alert,
"INSTALL".into(),
false,
)
.with_info_screen("FW FINGERPRINT".into(), fingerprint);
run(&mut frame) run(&mut frame)
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_confirm() -> u32 { extern "C" fn screen_wipe_confirm() -> u32 {
let message = let message = Label::left_aligned("Seed and firmware will be erased!".into(), TEXT_NORMAL)
Label::left_aligned("Seed and firmware will be erased!", TEXT_NORMAL).vertically_centered(); .vertically_centered();
let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET", false); let mut frame = Confirm::new(
BLD_BG,
"FACTORY RESET".into(),
message,
None,
"RESET".into(),
false,
);
run(&mut frame) run(&mut frame)
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_unlock_bootloader_confirm() -> u32 { extern "C" fn screen_unlock_bootloader_confirm() -> u32 {
let message = let message = Label::left_aligned("This action cannot be undone!".into(), TEXT_NORMAL)
Label::left_aligned("This action cannot be undone!", TEXT_NORMAL).vertically_centered(); .vertically_centered();
let mut frame = Confirm::new(BLD_BG, "UNLOCK BOOTLOADER?", message, None, "UNLOCK", true); let mut frame = Confirm::new(
BLD_BG,
"UNLOCK BOOTLOADER?".into(),
message,
None,
"UNLOCK".into(),
true,
);
run(&mut frame) run(&mut frame)
} }
#[no_mangle] #[no_mangle]
extern "C" fn screen_unlock_bootloader_success() { extern "C" fn screen_unlock_bootloader_success() {
let title = Label::centered("Bootloader unlocked", TEXT_BOLD).vertically_centered(); let title = Label::centered("Bootloader unlocked".into(), TEXT_BOLD).vertically_centered();
let content = let content =
Label::centered("Please reconnect the\ndevice", TEXT_NORMAL).vertically_centered(); Label::centered("Please reconnect the\ndevice".into(), TEXT_NORMAL).vertically_centered();
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
show(&mut frame, false); show(&mut frame, false);
@ -150,7 +174,11 @@ extern "C" fn screen_intro(
unwrap!(version_str.push_str("\nby ")); unwrap!(version_str.push_str("\nby "));
unwrap!(version_str.push_str(vendor)); unwrap!(version_str.push_str(vendor));
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok); let mut frame = Intro::new(
title_str.as_str().into(),
version_str.as_str().into(),
fw_ok,
);
run(&mut frame) run(&mut frame)
} }
@ -231,10 +259,10 @@ extern "C" fn screen_connect(_initial_setup: bool) {
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_success() { extern "C" fn screen_wipe_success() {
let title = Label::centered("Trezor Reset", TEXT_BOLD).vertically_centered(); let title = Label::centered("Trezor Reset".into(), TEXT_BOLD).vertically_centered();
let content = let content =
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL).vertically_centered();
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
show(&mut frame, false); show(&mut frame, false);
@ -242,10 +270,10 @@ extern "C" fn screen_wipe_success() {
#[no_mangle] #[no_mangle]
extern "C" fn screen_wipe_fail() { extern "C" fn screen_wipe_fail() {
let title = Label::centered("Reset failed", TEXT_BOLD).vertically_centered(); let title = Label::centered("Reset failed".into(), TEXT_BOLD).vertically_centered();
let content = let content =
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL).vertically_centered();
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
show(&mut frame, false); show(&mut frame, false);
@ -261,10 +289,10 @@ extern "C" fn screen_boot_empty(_fading: bool) {
#[no_mangle] #[no_mangle]
extern "C" fn screen_install_fail() { extern "C" fn screen_install_fail() {
let title = Label::centered("Install failed", TEXT_BOLD).vertically_centered(); let title = Label::centered("Install failed".into(), TEXT_BOLD).vertically_centered();
let content = let content =
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL).vertically_centered();
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
show(&mut frame, false); show(&mut frame, false);
@ -287,9 +315,9 @@ extern "C" fn screen_install_success(
unwrap!(reboot_msg.push_str("Reconnect the device")); unwrap!(reboot_msg.push_str("Reconnect the device"));
} }
let title = Label::centered("Firmware installed", TEXT_BOLD).vertically_centered(); let title = Label::centered("Firmware installed".into(), TEXT_BOLD).vertically_centered();
let content = Label::centered(reboot_msg.as_str(), TEXT_NORMAL).vertically_centered(); let content = Label::centered(reboot_msg.as_str().into(), TEXT_NORMAL).vertically_centered();
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw); let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw);
show(&mut frame, false); show(&mut frame, false);

@ -23,7 +23,7 @@ const QR_BORDER: i16 = 3;
pub struct AddressDetails { pub struct AddressDetails {
qr_code: Qr, qr_code: Qr,
details_view: Paragraphs<ParagraphVecShort<'static>>, details_view: Paragraphs<ParagraphVecShort<'static>>,
xpub_view: Frame<Paragraphs<Paragraph<'static>>, StrBuffer>, xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>, xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>,
current_page: usize, current_page: usize,
current_subpage: usize, current_subpage: usize,
@ -149,7 +149,7 @@ impl AddressDetails {
fn fill_xpub_page(&mut self, ctx: &mut EventCtx) { fn fill_xpub_page(&mut self, ctx: &mut EventCtx) {
let i = self.current_page - 2; let i = self.current_page - 2;
self.xpub_view.update_title(ctx, self.xpubs[i].0); self.xpub_view.update_title(ctx, self.xpubs[i].0.into());
self.xpub_view.update_content(ctx, |p| { self.xpub_view.update_content(ctx, |p| {
p.inner_mut().update(self.xpubs[i].1); p.inner_mut().update(self.xpubs[i].1);
p.change_page(0) p.change_page(0)

@ -20,14 +20,14 @@ pub enum ConfirmMsg {
Confirm = 2, Confirm = 2,
} }
pub struct Confirm<U> { pub struct Confirm<'a> {
bg: Pad, bg: Pad,
bg_color: Color, bg_color: Color,
title: TString<'static>, title: TString<'a>,
message: Child<Label<U>>, message: Child<Label<'a>>,
alert: Option<Label<U>>, alert: Option<Label<'a>>,
info_title: Option<TString<'static>>, info_title: Option<TString<'a>>,
info_text: Option<Label<U>>, info_text: Option<Label<'a>>,
button_text: TString<'static>, button_text: TString<'static>,
buttons: ButtonController, buttons: ButtonController,
/// Whether we are on the info screen (optional extra screen) /// Whether we are on the info screen (optional extra screen)
@ -35,25 +35,21 @@ pub struct Confirm<U> {
two_btn_confirm: bool, two_btn_confirm: bool,
} }
impl<U> Confirm<U> impl<'a> Confirm<'a> {
where pub fn new(
U: AsRef<str>,
{
pub fn new<T: Into<TString<'static>>>(
bg_color: Color, bg_color: Color,
title: T, title: TString<'a>,
message: Label<U>, message: Label<'a>,
alert: Option<Label<U>>, alert: Option<Label<'a>>,
button_text: T, button_text: TString<'static>,
two_btn_confirm: bool, two_btn_confirm: bool,
) -> Self { ) -> Self {
let button_text = button_text.into();
let btn_layout = let btn_layout =
Self::get_button_layout_general(false, button_text, false, two_btn_confirm); Self::get_button_layout_general(false, button_text, false, two_btn_confirm);
Self { Self {
bg: Pad::with_background(bg_color).with_clear(), bg: Pad::with_background(bg_color).with_clear(),
bg_color, bg_color,
title: title.into(), title,
message: Child::new(message), message: Child::new(message),
alert, alert,
info_title: None, info_title: None,
@ -66,12 +62,8 @@ where
} }
/// Adding optional info screen /// Adding optional info screen
pub fn with_info_screen<T: Into<TString<'static>>>( pub fn with_info_screen(mut self, info_title: TString<'a>, info_text: Label<'a>) -> Self {
mut self, self.info_title = Some(info_title);
info_title: T,
info_text: Label<U>,
) -> Self {
self.info_title = Some(info_title.into());
self.info_text = Some(info_text); self.info_text = Some(info_text);
self.buttons = ButtonController::new(self.get_button_layout()); self.buttons = ButtonController::new(self.get_button_layout());
self self
@ -125,10 +117,7 @@ where
} }
} }
impl<U> Component for Confirm<U> impl<'a> Component for Confirm<'a> {
where
U: AsRef<str>,
{
type Msg = ConfirmMsg; type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -207,7 +196,7 @@ where
fn paint(&mut self) { fn paint(&mut self) {
self.bg.paint(); self.bg.paint();
let display_top_left = |text: TString<'static>| { let display_top_left = |text: TString<'a>| {
text.map(|t| { text.map(|t| {
display::text_top_left(Point::zero(), t, Font::BOLD, WHITE, self.bg_color) display::text_top_left(Point::zero(), t, Font::BOLD, WHITE, self.bg_color)
}); });
@ -234,10 +223,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<U> crate::trace::Trace for Confirm<U> impl crate::trace::Trace for Confirm<'_> {
where
U: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("BlConfirm"); t.component("BlConfirm");
} }

@ -1,8 +1,11 @@
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx, Label, Never, Pad}, strutil::TString,
constant::{screen, WIDTH}, ui::{
display, component::{Child, Component, Event, EventCtx, Label, Never, Pad},
geometry::{Alignment2D, Offset, Point, Rect}, constant::{screen, WIDTH},
display,
geometry::{Alignment2D, Offset, Point, Rect},
},
}; };
use super::super::{ use super::super::{
@ -13,17 +16,17 @@ use super::super::{
const FOOTER_AREA_HEIGHT: i16 = 20; const FOOTER_AREA_HEIGHT: i16 = 20;
const DIVIDER_POSITION: i16 = 43; const DIVIDER_POSITION: i16 = 43;
pub struct ErrorScreen<T> { pub struct ErrorScreen<'a> {
bg: Pad, bg: Pad,
show_icons: bool, show_icons: bool,
title: Child<Label<T>>, title: Child<Label<'a>>,
message: Child<Label<T>>, message: Child<Label<'a>>,
footer: Child<Label<T>>, footer: Child<Label<'a>>,
area: Rect, area: Rect,
} }
impl<T: AsRef<str>> ErrorScreen<T> { impl<'a> ErrorScreen<'a> {
pub fn new(title: T, message: T, footer: T) -> Self { pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self {
let title = Label::centered(title, theme::TEXT_BOLD); let title = Label::centered(title, theme::TEXT_BOLD);
let message = Label::centered(message, theme::TEXT_NORMAL).vertically_centered(); let message = Label::centered(message, theme::TEXT_NORMAL).vertically_centered();
let footer = Label::centered(footer, theme::TEXT_NORMAL).vertically_centered(); let footer = Label::centered(footer, theme::TEXT_NORMAL).vertically_centered();
@ -39,7 +42,7 @@ impl<T: AsRef<str>> ErrorScreen<T> {
} }
} }
impl<T: AsRef<str>> Component for ErrorScreen<T> { impl Component for ErrorScreen<'_> {
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::StringType, strutil::TString,
ui::{ ui::{
component::{Child, Component, ComponentExt, Event, EventCtx, Pad, Paginate}, component::{Child, Component, ComponentExt, Event, EventCtx, Pad, Paginate},
geometry::Rect, geometry::Rect,
@ -11,17 +11,16 @@ use super::{
ButtonControllerMsg, ButtonLayout, ButtonPos, CancelInfoConfirmMsg, FlowPages, Page, ScrollBar, ButtonControllerMsg, ButtonLayout, ButtonPos, CancelInfoConfirmMsg, FlowPages, Page, ScrollBar,
}; };
pub struct Flow<F, T> pub struct Flow<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
/// Function to get pages from /// Function to get pages from
pages: FlowPages<F, T>, pages: FlowPages<F>,
/// Instance of the current Page /// Instance of the current Page
current_page: Page<T>, current_page: Page,
/// Title being shown at the top in bold /// Title being shown at the top in bold
title: Option<Title<T>>, title: Option<Title>,
scrollbar: Child<ScrollBar>, scrollbar: Child<ScrollBar>,
content_area: Rect, content_area: Rect,
title_area: Rect, title_area: Rect,
@ -35,12 +34,11 @@ where
ignore_second_button_ms: Option<u32>, ignore_second_button_ms: Option<u32>,
} }
impl<F, T> Flow<F, T> impl<F> Flow<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
pub fn new(pages: FlowPages<F, T>) -> Self { pub fn new(pages: FlowPages<F>) -> Self {
let current_page = pages.get(0); let current_page = pages.get(0);
let title = current_page.title().map(Title::new); let title = current_page.title().map(Title::new);
Self { Self {
@ -64,7 +62,7 @@ where
/// Adding a common title to all pages. The title will not be colliding /// Adding a common title to all pages. The title will not be colliding
/// with the page content, as the content will be offset. /// with the page content, as the content will be offset.
pub fn with_common_title(mut self, title: T) -> Self { pub fn with_common_title(mut self, title: TString<'static>) -> Self {
self.title = Some(Title::new(title)); self.title = Some(Title::new(title));
self self
} }
@ -196,10 +194,9 @@ where
} }
} }
impl<F, T> Component for Flow<F, T> impl<F> Component for Flow<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
type Msg = CancelInfoConfirmMsg; type Msg = CancelInfoConfirmMsg;
@ -313,10 +310,9 @@ where
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<F, T> crate::trace::Trace for Flow<F, T> impl<F> crate::trace::Trace for Flow<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Flow"); t.component("Flow");

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::StringType, strutil::TString,
ui::{ ui::{
component::{base::Component, FormattedText, Paginate}, component::{base::Component, FormattedText, Paginate},
geometry::Rect, geometry::Rect,
@ -21,10 +21,9 @@ const MAX_OPS_PER_PAGE: usize = 15;
/// have theoretically unlimited number of pages without triggering SO. /// have theoretically unlimited number of pages without triggering SO.
/// (Currently only the current page is stored on stack - in /// (Currently only the current page is stored on stack - in
/// `Flow::current_page`.) /// `Flow::current_page`.)
pub struct FlowPages<F, T> pub struct FlowPages<F>
where where
T: StringType + Clone, F: Fn(usize) -> Page,
F: Fn(usize) -> Page<T>,
{ {
/// Function/closure that will return appropriate page on demand. /// Function/closure that will return appropriate page on demand.
get_page: F, get_page: F,
@ -32,10 +31,9 @@ where
page_count: usize, page_count: usize,
} }
impl<F, T> FlowPages<F, T> impl<F> FlowPages<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
pub fn new(get_page: F, page_count: usize) -> Self { pub fn new(get_page: F, page_count: usize) -> Self {
Self { Self {
@ -45,7 +43,7 @@ where
} }
/// Returns a page on demand on a specified index. /// Returns a page on demand on a specified index.
pub fn get(&self, page_index: usize) -> Page<T> { pub fn get(&self, page_index: usize) -> Page {
(self.get_page)(page_index) (self.get_page)(page_index)
} }
@ -74,24 +72,18 @@ where
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Page<T> pub struct Page {
where
T: StringType + Clone,
{
formatted: FormattedText, formatted: FormattedText,
btn_layout: ButtonLayout, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
current_page: usize, current_page: usize,
page_count: usize, page_count: usize,
title: Option<T>, title: Option<TString<'static>>,
slim_arrows: bool, slim_arrows: bool,
} }
// For `layout.rs` // For `layout.rs`
impl<T> Page<T> impl Page {
where
T: StringType + Clone,
{
pub fn new( pub fn new(
btn_layout: ButtonLayout, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
@ -110,12 +102,9 @@ where
} }
// For `flow.rs` // For `flow.rs`
impl<T> Page<T> impl Page {
where
T: StringType + Clone,
{
/// Adding title. /// Adding title.
pub fn with_title(mut self, title: T) -> Self { pub fn with_title(mut self, title: TString<'static>) -> Self {
self.title = Some(title); self.title = Some(title);
self self
} }
@ -179,7 +168,7 @@ where
self.btn_actions self.btn_actions
} }
pub fn title(&self) -> Option<T> { pub fn title(&self) -> Option<TString<'static>> {
self.title.clone() self.title.clone()
} }
@ -205,10 +194,7 @@ where
} }
// Pagination // Pagination
impl<T> Paginate for Page<T> impl Paginate for Page {
where
T: StringType + Clone,
{
fn page_count(&mut self) -> usize { fn page_count(&mut self) -> usize {
self.formatted.page_count() self.formatted.page_count()
} }
@ -224,10 +210,7 @@ where
use crate::ui::component::text::layout::LayoutFit; use crate::ui::component::text::layout::LayoutFit;
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Page<T> impl crate::trace::Trace for Page {
where
T: StringType + Clone,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
use crate::ui::component::text::layout::trace::TraceSink; use crate::ui::component::text::layout::trace::TraceSink;
use core::cell::Cell; use core::cell::Cell;
@ -235,7 +218,7 @@ where
t.component("Page"); t.component("Page");
if let Some(title) = &self.title { if let Some(title) = &self.title {
// Not calling it "title" as that is already traced by FlowPage // Not calling it "title" as that is already traced by FlowPage
t.string("page_title", title.as_ref().into()); t.string("page_title", *title);
} }
t.int("active_page", self.current_page as i64); t.int("active_page", self.current_page as i64);
t.int("page_count", self.page_count as i64); t.int("page_count", self.page_count as i64);

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::StringType, strutil::TString,
ui::{ ui::{
component::{Child, Component, ComponentExt, Event, EventCtx, Paginate}, component::{Child, Component, ComponentExt, Event, EventCtx, Paginate},
geometry::{Insets, Rect}, geometry::{Insets, Rect},
@ -9,21 +9,19 @@ use crate::{
use super::{super::constant, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar}; use super::{super::constant, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar};
/// Component for holding another component and displaying a title. /// Component for holding another component and displaying a title.
pub struct Frame<T, U> pub struct Frame<T>
where where
T: Component, T: Component,
U: StringType,
{ {
title: Title<U>, title: Title,
content: Child<T>, content: Child<T>,
} }
impl<T, U> Frame<T, U> impl<T> Frame<T>
where where
T: Component, T: Component,
U: StringType + Clone,
{ {
pub fn new(title: U, content: T) -> Self { pub fn new(title: TString<'static>, content: T) -> Self {
Self { Self {
title: Title::new(title), title: Title::new(title),
content: Child::new(content), content: Child::new(content),
@ -40,7 +38,7 @@ where
self.content.inner() self.content.inner()
} }
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: U) { pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: TString<'static>) {
self.title.set_text(ctx, new_title); self.title.set_text(ctx, new_title);
} }
@ -56,10 +54,9 @@ where
} }
} }
impl<T, U> Component for Frame<T, U> impl<T> Component for Frame<T>
where where
T: Component, T: Component,
U: StringType + Clone,
{ {
type Msg = T::Msg; type Msg = T::Msg;
@ -85,10 +82,9 @@ where
} }
} }
impl<T, U> Paginate for Frame<T, U> impl<T> Paginate for Frame<T>
where where
T: Component + Paginate, T: Component + Paginate,
U: StringType + Clone,
{ {
fn page_count(&mut self) -> usize { fn page_count(&mut self) -> usize {
self.content.page_count() self.content.page_count()
@ -106,20 +102,18 @@ pub trait ScrollableContent {
/// Component for holding another component and displaying a title. /// Component for holding another component and displaying a title.
/// Also is allocating space for a scrollbar. /// Also is allocating space for a scrollbar.
pub struct ScrollableFrame<T, U> pub struct ScrollableFrame<T>
where where
T: Component + ScrollableContent, T: Component + ScrollableContent,
U: StringType + Clone,
{ {
title: Option<Child<Title<U>>>, title: Option<Child<Title>>,
scrollbar: ScrollBar, scrollbar: ScrollBar,
content: Child<T>, content: Child<T>,
} }
impl<T, U> ScrollableFrame<T, U> impl<T> ScrollableFrame<T>
where where
T: Component + ScrollableContent, T: Component + ScrollableContent,
U: StringType + Clone,
{ {
pub fn new(content: T) -> Self { pub fn new(content: T) -> Self {
Self { Self {
@ -133,16 +127,15 @@ where
self.content.inner() self.content.inner()
} }
pub fn with_title(mut self, title: U) -> Self { pub fn with_title(mut self, title: TString<'static>) -> Self {
self.title = Some(Child::new(Title::new(title))); self.title = Some(Child::new(Title::new(title)));
self self
} }
} }
impl<T, U> Component for ScrollableFrame<T, U> impl<T> Component for ScrollableFrame<T>
where where
T: Component + ScrollableContent, T: Component + ScrollableContent,
U: StringType + Clone,
{ {
type Msg = T::Msg; type Msg = T::Msg;
@ -209,10 +202,9 @@ where
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for Frame<T, U> impl<T> crate::trace::Trace for Frame<T>
where where
T: crate::trace::Trace + Component, T: crate::trace::Trace + Component,
U: StringType + Clone,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Frame"); t.component("Frame");
@ -222,10 +214,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for ScrollableFrame<T, U> impl<T> crate::trace::Trace for ScrollableFrame<T>
where where
T: crate::trace::Trace + Component + ScrollableContent, T: crate::trace::Trace + Component + ScrollableContent,
U: StringType + Clone,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ScrollableFrame"); t.component("ScrollableFrame");

@ -1,7 +1,5 @@
use crate::{ use crate::{
error::Error, strutil::TString,
micropython::buffer::StrBuffer,
strutil::StringType,
translations::TR, translations::TR,
trezorhal::usb::usb_configured, trezorhal::usb::usb_configured,
ui::{ ui::{
@ -54,34 +52,32 @@ enum CurrentScreen {
Loader, Loader,
} }
pub struct Homescreen<T> pub struct Homescreen {
where
T: StringType,
{
// TODO label should be a Child in theory, but the homescreen image is not, so it is // TODO label should be a Child in theory, but the homescreen image is not, so it is
// always painted, so we need to always paint the label too // always painted, so we need to always paint the label too
label: Label<T>, label: Label<'static>,
notification: Option<(T, u8)>, notification: Option<(TString<'static>, u8)>,
/// Used for HTC functionality to lock device from homescreen /// Used for HTC functionality to lock device from homescreen
invisible_buttons: Child<ButtonController>, invisible_buttons: Child<ButtonController>,
/// Holds the loader component /// Holds the loader component
loader: Option<Child<ProgressLoader<T>>>, loader: Option<Child<ProgressLoader>>,
/// Whether to show the loader or not /// Whether to show the loader or not
show_loader: bool, show_loader: bool,
/// Which screen is currently shown /// Which screen is currently shown
current_screen: CurrentScreen, current_screen: CurrentScreen,
} }
impl<T> Homescreen<T> impl Homescreen {
where pub fn new(
T: StringType + Clone, label: TString<'static>,
{ notification: Option<(TString<'static>, u8)>,
pub fn new(label: T, notification: Option<(T, u8)>, loader_description: Option<T>) -> Self { loader_description: Option<TString<'static>>,
) -> Self {
// Buttons will not be visible, we only need both left and right to be existing // Buttons will not be visible, we only need both left and right to be existing
// so we can get the events from them. // so we can get the events from them.
let invisible_btn_layout = ButtonLayout::text_none_text("".into(), "".into()); let invisible_btn_layout = ButtonLayout::text_none_text("".into(), "".into());
let loader = let loader = loader_description
loader_description.map(|desc| Child::new(ProgressLoader::new(desc, HOLD_TO_LOCK_MS))); .map(|desc| Child::new(ProgressLoader::new(desc.into(), HOLD_TO_LOCK_MS)));
Self { Self {
label: Label::centered(label, theme::TEXT_BIG), label: Label::centered(label, theme::TEXT_BIG),
notification, notification,
@ -114,11 +110,11 @@ where
.map_translated(|t| display_center(baseline, t, NOTIFICATION_FONT)); .map_translated(|t| display_center(baseline, t, NOTIFICATION_FONT));
} else if let Some((notification, _level)) = &self.notification { } else if let Some((notification, _level)) = &self.notification {
self.fill_notification_background(); self.fill_notification_background();
display_center(baseline, notification.as_ref(), NOTIFICATION_FONT); display_center(baseline, notification.map(|c| c), NOTIFICATION_FONT);
// Painting warning icons in top corners when the text is short enough not to // Painting warning icons in top corners when the text is short enough not to
// collide with them // collide with them
let icon_width = NOTIFICATION_ICON.toif.width(); let icon_width = NOTIFICATION_ICON.toif.width();
let text_width = NOTIFICATION_FONT.text_width(notification.as_ref()); let text_width = NOTIFICATION_FONT.text_width(notification.map(|c| c));
if AREA.width() >= text_width + (icon_width + 1) * 2 { if AREA.width() >= text_width + (icon_width + 1) * 2 {
NOTIFICATION_ICON.draw( NOTIFICATION_ICON.draw(
AREA.top_left(), AREA.top_left(),
@ -169,10 +165,7 @@ where
} }
} }
impl<T> Component for Homescreen<T> impl Component for Homescreen {
where
T: StringType + Clone,
{
type Msg = (); type Msg = ();
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -238,12 +231,9 @@ where
} }
} }
pub struct Lockscreen<T> pub struct Lockscreen<'a> {
where label: Child<Label<'a>>,
T: StringType, instruction: Child<Label<'static>>,
{
label: Child<Label<T>>,
instruction: Child<Label<StrBuffer>>,
/// Used for unlocking the device from lockscreen /// Used for unlocking the device from lockscreen
invisible_buttons: Child<ButtonController>, invisible_buttons: Child<ButtonController>,
/// Display coinjoin icon? /// Display coinjoin icon?
@ -252,11 +242,8 @@ where
screensaver: bool, screensaver: bool,
} }
impl<T> Lockscreen<T> impl<'a> Lockscreen<'a> {
where pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
T: StringType + Clone,
{
pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Result<Self, Error> {
// Buttons will not be visible, we only need all three of them to be present, // Buttons will not be visible, we only need all three of them to be present,
// so that even middle-click triggers the event. // so that even middle-click triggers the event.
let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into()); let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into());
@ -265,23 +252,17 @@ where
} else { } else {
TR::homescreen__click_to_unlock TR::homescreen__click_to_unlock
}; };
Ok(Lockscreen { Self {
label: Child::new(Label::centered(label, theme::TEXT_BIG)), label: Child::new(Label::centered(label, theme::TEXT_BIG)),
instruction: Child::new(Label::centered( instruction: Child::new(Label::centered(instruction_str.into(), theme::TEXT_NORMAL)),
instruction_str.try_into()?,
theme::TEXT_NORMAL,
)),
invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)), invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)),
coinjoin_icon: coinjoin_authorized.then_some(theme::ICON_COINJOIN), coinjoin_icon: coinjoin_authorized.then_some(theme::ICON_COINJOIN),
screensaver: !bootscreen, screensaver: !bootscreen,
}) }
} }
} }
impl<T> Component for Lockscreen<T> impl Component for Lockscreen<'_> {
where
T: StringType + Clone,
{
type Msg = (); type Msg = ();
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -322,20 +303,14 @@ where
} }
} }
pub struct ConfirmHomescreen<T, F> pub struct ConfirmHomescreen<F> {
where title: Child<Label<'static>>,
T: StringType,
{
title: Child<Label<T>>,
buffer_func: F, buffer_func: F,
buttons: Child<ButtonController>, buttons: Child<ButtonController>,
} }
impl<T, F> ConfirmHomescreen<T, F> impl<F> ConfirmHomescreen<F> {
where pub fn new(title: TString<'static>, buffer_func: F) -> Self {
T: StringType + Clone,
{
pub fn new(title: T, buffer_func: F) -> Self {
let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into()); let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into());
ConfirmHomescreen { ConfirmHomescreen {
title: Child::new(Label::centered(title, theme::TEXT_BOLD)), title: Child::new(Label::centered(title, theme::TEXT_BOLD)),
@ -345,9 +320,8 @@ where
} }
} }
impl<'a, T, F> Component for ConfirmHomescreen<T, F> impl<'a, F> Component for ConfirmHomescreen<F>
where where
T: StringType + Clone,
F: Fn() -> &'a [u8], F: Fn() -> &'a [u8],
{ {
type Msg = CancelConfirmMsg; type Msg = CancelConfirmMsg;
@ -398,10 +372,7 @@ pub fn check_homescreen_format(toif: &Toif) -> bool {
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Homescreen<T> impl crate::trace::Trace for Homescreen {
where
T: StringType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Homescreen"); t.component("Homescreen");
t.child("label", &self.label); t.child("label", &self.label);
@ -409,10 +380,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Lockscreen<T> impl crate::trace::Trace for Lockscreen<'_> {
where
T: StringType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Lockscreen"); t.component("Lockscreen");
t.child("label", &self.label); t.child("label", &self.label);
@ -420,10 +388,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, F> crate::trace::Trace for ConfirmHomescreen<T, F> impl<F> crate::trace::Trace for ConfirmHomescreen<F> {
where
T: StringType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("ConfirmHomescreen"); t.component("ConfirmHomescreen");
t.child("title", &self.title); t.child("title", &self.title);

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::{StringType, TString}, strutil::TString,
translations::TR, translations::TR,
trezorhal::random, trezorhal::random,
ui::{ ui::{
@ -129,12 +129,12 @@ impl ChoiceFactory for ChoiceFactoryPIN {
} }
/// Component for entering a PIN. /// Component for entering a PIN.
pub struct PinEntry<T: StringType + Clone> { pub struct PinEntry<'a> {
choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>, choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>,
header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>, header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>, pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
prompt: T, prompt: TString<'a>,
subprompt: T, subprompt: TString<'a>,
/// Whether we already show the "real" prompt (not the warning). /// Whether we already show the "real" prompt (not the warning).
showing_real_prompt: bool, showing_real_prompt: bool,
show_real_pin: bool, show_real_pin: bool,
@ -142,26 +142,23 @@ pub struct PinEntry<T: StringType + Clone> {
textbox: TextBox<MAX_PIN_LENGTH>, textbox: TextBox<MAX_PIN_LENGTH>,
} }
impl<T> PinEntry<T> impl<'a> PinEntry<'a> {
where pub fn new(prompt: TString<'a>, subprompt: TString<'a>) -> Self {
T: StringType + Clone,
{
pub fn new(prompt: T, subprompt: T) -> Self {
// When subprompt is not empty, it means that the user has entered bad PIN // When subprompt is not empty, it means that the user has entered bad PIN
// before. In this case we show the warning together with the subprompt // before. In this case we show the warning together with the subprompt
// at the beginning. (WRONG PIN will be replaced by real prompt after // at the beginning. (WRONG PIN will be replaced by real prompt after
// any button click.) // any button click.)
let show_subprompt = !subprompt.as_ref().is_empty(); let show_subprompt = !subprompt.is_empty();
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt { let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
( (
false, false,
TR::pin__title_wrong_pin.map_translated(|t| String::from(t)), TR::pin__title_wrong_pin.map_translated(|t| String::from(t)),
String::from(subprompt.as_ref()), String::from(subprompt.map(|t| t)),
) )
} else { } else {
( (
true, true,
String::from(prompt.as_ref()), String::from(prompt.map(|t| t)),
String::from(EMPTY_PIN_STR), String::from(EMPTY_PIN_STR),
) )
}; };
@ -201,10 +198,10 @@ where
/// Many possibilities, according to the PIN state. /// Many possibilities, according to the PIN state.
fn update_pin_line(&mut self, ctx: &mut EventCtx) { fn update_pin_line(&mut self, ctx: &mut EventCtx) {
let mut used_font = Font::BOLD; let mut used_font = Font::BOLD;
let pin_line_text = if self.is_empty() && !self.subprompt.as_ref().is_empty() { let pin_line_text = if self.is_empty() && !self.subprompt.is_empty() {
// Showing the subprompt in NORMAL font // Showing the subprompt in NORMAL font
used_font = Font::NORMAL; used_font = Font::NORMAL;
String::from(self.subprompt.as_ref()) String::from(self.subprompt.map(|t| t))
} else if self.is_empty() { } else if self.is_empty() {
String::from(EMPTY_PIN_STR) String::from(EMPTY_PIN_STR)
} else if self.show_real_pin { } else if self.show_real_pin {
@ -234,7 +231,7 @@ where
/// Showing the real prompt instead of WRONG PIN /// Showing the real prompt instead of WRONG PIN
fn show_prompt(&mut self, ctx: &mut EventCtx) { fn show_prompt(&mut self, ctx: &mut EventCtx) {
self.header_line.mutate(ctx, |ctx, header_line| { self.header_line.mutate(ctx, |ctx, header_line| {
header_line.update_text(String::from(self.prompt.as_ref())); header_line.update_text(String::from(self.prompt.map(|t| t)));
header_line.request_complete_repaint(ctx); header_line.request_complete_repaint(ctx);
}); });
} }
@ -252,10 +249,7 @@ where
} }
} }
impl<T> Component for PinEntry<T> impl Component for PinEntry<'_> {
where
T: StringType + Clone,
{
type Msg = CancelConfirmMsg; type Msg = CancelConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -334,13 +328,10 @@ where
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for PinEntry<T> impl crate::trace::Trace for PinEntry<'_> {
where
T: StringType + Clone,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PinKeyboard"); t.component("PinKeyboard");
t.string("subprompt", self.subprompt.as_ref().into()); t.string("subprompt", self.subprompt.into());
t.string("pin", self.textbox.content().into()); t.string("pin", self.textbox.content().into());
t.child("choice_page", &self.choice_page); t.child("choice_page", &self.choice_page);
} }

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::{StringType, TString}, strutil::TString,
time::{Duration, Instant}, time::{Duration, Instant},
ui::{ ui::{
animation::Animation, animation::Animation,
@ -247,22 +247,16 @@ impl LoaderStyleSheet {
} }
} }
pub struct ProgressLoader<T> pub struct ProgressLoader {
where loader: Child<Progress>,
T: StringType,
{
loader: Child<Progress<T>>,
duration_ms: u32, duration_ms: u32,
start_time: Option<Instant>, start_time: Option<Instant>,
} }
impl<T> ProgressLoader<T> impl ProgressLoader {
where
T: StringType + Clone,
{
const LOADER_FRAMES_DEFAULT: u32 = 20; const LOADER_FRAMES_DEFAULT: u32 = 20;
pub fn new(loader_description: T, duration_ms: u32) -> Self { pub fn new(loader_description: TString<'static>, duration_ms: u32) -> Self {
Self { Self {
loader: Child::new( loader: Child::new(
Progress::new(false, loader_description).with_icon(theme::ICON_LOCK_SMALL), Progress::new(false, loader_description).with_icon(theme::ICON_LOCK_SMALL),
@ -300,10 +294,7 @@ where
} }
} }
impl<T> Component for ProgressLoader<T> impl Component for ProgressLoader {
where
T: StringType + Clone,
{
type Msg = LoaderMsg; type Msg = LoaderMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {

@ -1,7 +1,7 @@
use core::mem; use core::mem;
use crate::{ use crate::{
strutil::StringType, strutil::TString,
ui::{ ui::{
component::{ component::{
paginated::Paginate, paginated::Paginate,
@ -21,11 +21,8 @@ const BOTTOM_DESCRIPTION_MARGIN: i16 = 10;
const LOADER_Y_OFFSET_TITLE: i16 = -10; const LOADER_Y_OFFSET_TITLE: i16 = -10;
const LOADER_Y_OFFSET_NO_TITLE: i16 = -20; const LOADER_Y_OFFSET_NO_TITLE: i16 = -20;
pub struct Progress<T> pub struct Progress {
where title: Option<Child<Label<'static>>>,
T: StringType,
{
title: Option<Child<Label<T>>>,
value: u16, value: u16,
loader_y_offset: i16, loader_y_offset: i16,
indeterminate: bool, indeterminate: bool,
@ -34,13 +31,10 @@ where
icon: Icon, icon: Icon,
} }
impl<T> Progress<T> impl Progress {
where
T: StringType,
{
const AREA: Rect = constant::screen(); const AREA: Rect = constant::screen();
pub fn new(indeterminate: bool, description: T) -> Self { pub fn new(indeterminate: bool, description: TString<'static>) -> Self {
Self { Self {
title: None, title: None,
value: 0, value: 0,
@ -54,7 +48,7 @@ where
} }
} }
pub fn with_title(mut self, title: T) -> Self { pub fn with_title(mut self, title: TString<'static>) -> Self {
self.title = Some(Child::new(Label::centered(title, theme::TEXT_BOLD))); self.title = Some(Child::new(Label::centered(title, theme::TEXT_BOLD)));
self self
} }
@ -79,10 +73,7 @@ where
} }
} }
impl<T> Component for Progress<T> impl Component for Progress {
where
T: StringType,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect { fn place(&mut self, _bounds: Rect) -> Rect {
@ -95,7 +86,7 @@ where
let no_title_case = (Rect::zero(), Self::AREA, LOADER_Y_OFFSET_NO_TITLE); let no_title_case = (Rect::zero(), Self::AREA, LOADER_Y_OFFSET_NO_TITLE);
let (title, rest, loader_y_offset) = if let Some(self_title) = &self.title { let (title, rest, loader_y_offset) = if let Some(self_title) = &self.title {
if !self_title.inner().text().as_ref().is_empty() { if !self_title.inner().text().map(|c| c).is_empty() {
let (title, rest) = Self::AREA.split_top(self_title.inner().max_size().y); let (title, rest) = Self::AREA.split_top(self_title.inner().max_size().y);
(title, rest, LOADER_Y_OFFSET_TITLE) (title, rest, LOADER_Y_OFFSET_TITLE)
} else { } else {
@ -171,10 +162,7 @@ where
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Progress<T> impl crate::trace::Trace for Progress {
where
T: StringType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Progress"); t.component("Progress");
} }

@ -17,8 +17,8 @@ pub struct ResultScreen<'a> {
fg_color: Color, fg_color: Color,
bg_color: Color, bg_color: Color,
icon: Icon, icon: Icon,
message_top: Child<Label<&'static str>>, message_top: Child<Label<'static>>,
message_bottom: Child<Label<&'a str>>, message_bottom: Child<Label<'a>>,
} }
impl<'a> ResultScreen<'a> { impl<'a> ResultScreen<'a> {
@ -26,8 +26,8 @@ impl<'a> ResultScreen<'a> {
fg_color: Color, fg_color: Color,
bg_color: Color, bg_color: Color,
icon: Icon, icon: Icon,
title: Label<&'static str>, title: Label<'static>,
content: Label<&'a str>, content: Label<'a>,
complete_draw: bool, complete_draw: bool,
) -> Self { ) -> Self {
let mut instance = Self { let mut instance = Self {
@ -49,7 +49,7 @@ impl<'a> ResultScreen<'a> {
} }
} }
impl<'a> Component for ResultScreen<'a> { impl Component for ResultScreen<'_> {
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {

@ -25,7 +25,7 @@ pub struct ResultPopup {
area: Rect, area: Rect,
pad: Pad, pad: Pad,
result_anim: Child<ResultAnim>, result_anim: Child<ResultAnim>,
headline: Option<Label<&'static str>>, headline: Option<Label<'static>>,
text: Child<Paragraphs<Paragraph<'static>>>, text: Child<Paragraphs<Paragraph<'static>>>,
buttons: Option<Child<ButtonController>>, buttons: Option<Child<ButtonController>>,
autoclose: bool, autoclose: bool,
@ -59,7 +59,7 @@ impl ResultPopup {
area: Rect::zero(), area: Rect::zero(),
pad, pad,
result_anim: Child::new(ResultAnim::new(icon)), result_anim: Child::new(ResultAnim::new(icon)),
headline: headline.map(|a| Label::centered(a, theme::TEXT_BOLD)), headline: headline.map(|a| Label::centered(a.into(), theme::TEXT_BOLD)),
text: Child::new(p1), text: Child::new(p1),
buttons, buttons,
autoclose: false, autoclose: false,

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::StringType, strutil::TString,
time::Instant, time::Instant,
ui::{ ui::{
component::{Component, Event, EventCtx, Marquee, Never}, component::{Component, Event, EventCtx, Marquee, Never},
@ -10,22 +10,16 @@ use crate::{
use super::super::theme; use super::super::theme;
pub struct Title<T> pub struct Title {
where
T: StringType,
{
area: Rect, area: Rect,
title: T, title: TString<'static>,
marquee: Marquee<T>, marquee: Marquee,
needs_marquee: bool, needs_marquee: bool,
centered: bool, centered: bool,
} }
impl<T> Title<T> impl Title {
where pub fn new(title: TString<'static>) -> Self {
T: StringType + Clone,
{
pub fn new(title: T) -> Self {
Self { Self {
title: title.clone(), title: title.clone(),
marquee: Marquee::new(title, theme::FONT_HEADER, theme::FG, theme::BG), marquee: Marquee::new(title, theme::FONT_HEADER, theme::FG, theme::BG),
@ -41,13 +35,13 @@ where
} }
pub fn get_text(&self) -> &str { pub fn get_text(&self) -> &str {
self.title.as_ref() self.title.map(|s| s.as_ref())
} }
pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: T) { pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: TString<'static>) {
self.title = new_text.clone(); self.title = new_text.clone();
self.marquee.set_text(new_text.clone()); self.marquee.set_text(new_text.clone());
let text_width = theme::FONT_HEADER.text_width(new_text.as_ref()); let text_width = theme::FONT_HEADER.text_width(new_text.map(|s| s.as_ref()));
self.needs_marquee = text_width > self.area.width(); self.needs_marquee = text_width > self.area.width();
// Resetting the marquee to the beginning and starting it when necessary. // Resetting the marquee to the beginning and starting it when necessary.
self.marquee.reset(); self.marquee.reset();
@ -57,12 +51,12 @@ where
} }
/// Display title/header at the top left of the given area. /// Display title/header at the top left of the given area.
pub fn paint_header_left(title: &T, area: Rect) { pub fn paint_header_left(title: &TString<'static>, area: Rect) {
let text_height = theme::FONT_HEADER.text_height(); let text_height = theme::FONT_HEADER.text_height();
let title_baseline = area.top_left() + Offset::y(text_height - 1); let title_baseline = area.top_left() + Offset::y(text_height - 1);
display::text_left( display::text_left(
title_baseline, title_baseline,
title.as_ref(), title.map(|s| s.as_ref()),
theme::FONT_HEADER, theme::FONT_HEADER,
theme::FG, theme::FG,
theme::BG, theme::BG,
@ -70,12 +64,12 @@ where
} }
/// Display title/header centered at the top of the given area. /// Display title/header centered at the top of the given area.
pub fn paint_header_centered(title: &T, area: Rect) { pub fn paint_header_centered(title: &TString<'static>, area: Rect) {
let text_height = theme::FONT_HEADER.text_height(); let text_height = theme::FONT_HEADER.text_height();
let title_baseline = area.top_center() + Offset::y(text_height - 1); let title_baseline = area.top_center() + Offset::y(text_height - 1);
display::text_center( display::text_center(
title_baseline, title_baseline,
title.as_ref(), title.map(|s| s.as_ref()),
theme::FONT_HEADER, theme::FONT_HEADER,
theme::FG, theme::FG,
theme::BG, theme::BG,
@ -83,16 +77,13 @@ where
} }
} }
impl<T> Component for Title<T> impl Component for Title {
where
T: StringType + Clone,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds; self.area = bounds;
self.marquee.place(bounds); self.marquee.place(bounds);
let width = theme::FONT_HEADER.text_width(self.title.as_ref()); let width = theme::FONT_HEADER.text_width(self.title.map(|s| s.as_ref()));
self.needs_marquee = width > self.area.width(); self.needs_marquee = width > self.area.width();
bounds bounds
} }
@ -121,12 +112,9 @@ where
// DEBUG-ONLY SECTION BELOW // DEBUG-ONLY SECTION BELOW
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Title<T> impl crate::trace::Trace for Title {
where
T: StringType + Clone,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Title"); t.component("Title");
t.string("text", self.title.as_ref().into()); t.string("text", self.title.map(|t| t).into());
} }
} }

@ -99,10 +99,9 @@ where
} }
} }
impl<F, T> ComponentMsgObj for Flow<F, T> impl<F> ComponentMsgObj for Flow<F>
where where
F: Fn(usize) -> Page<T>, F: Fn(usize) -> Page,
T: StringType + Clone,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -119,10 +118,7 @@ where
} }
} }
impl<T> ComponentMsgObj for PinEntry<T> impl ComponentMsgObj for PinEntry<'_> {
where
T: StringType + Clone,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
CancelConfirmMsg::Confirmed => self.pin().try_into(), CancelConfirmMsg::Confirmed => self.pin().try_into(),
@ -187,56 +183,44 @@ impl ComponentMsgObj for PassphraseEntry {
} }
} }
impl<T, U> ComponentMsgObj for Frame<T, U> impl<T> ComponentMsgObj for Frame<T>
where where
T: ComponentMsgObj, T: ComponentMsgObj,
U: StringType + Clone,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg) self.inner().msg_try_into_obj(msg)
} }
} }
impl<T, U> ComponentMsgObj for ScrollableFrame<T, U> impl<T> ComponentMsgObj for ScrollableFrame<T>
where where
T: ComponentMsgObj + ScrollableContent, T: ComponentMsgObj + ScrollableContent,
U: StringType + Clone,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
self.inner().msg_try_into_obj(msg) self.inner().msg_try_into_obj(msg)
} }
} }
impl<T> ComponentMsgObj for Progress<T> impl ComponentMsgObj for Progress {
where
T: StringType,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!() unreachable!()
} }
} }
impl<T> ComponentMsgObj for Homescreen<T> impl ComponentMsgObj for Homescreen {
where
T: StringType + Clone,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
Ok(CANCELLED.as_obj()) Ok(CANCELLED.as_obj())
} }
} }
impl<T> ComponentMsgObj for Lockscreen<T> impl<'a> ComponentMsgObj for Lockscreen<'a> {
where
T: StringType + Clone,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
Ok(CANCELLED.as_obj()) Ok(CANCELLED.as_obj())
} }
} }
impl<'a, T, F> ComponentMsgObj for ConfirmHomescreen<T, F> impl<'a, F> ComponentMsgObj for ConfirmHomescreen<F>
where where
T: StringType + Clone,
F: Fn() -> &'a [u8], F: Fn() -> &'a [u8],
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
@ -247,10 +231,7 @@ where
} }
} }
impl<U> ComponentMsgObj for super::component::bl_confirm::Confirm<U> impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
where
U: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()), super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
@ -290,7 +271,7 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
let mut frame = ScrollableFrame::new(content); let mut frame = ScrollableFrame::new(content);
if !title.as_ref().is_empty() { if !title.as_ref().is_empty() {
frame = frame.with_title(title); frame = frame.with_title(title.into());
} }
let obj = LayoutObj::new(frame)?; let obj = LayoutObj::new(frame)?;
@ -430,7 +411,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
// discarded before returning to micropython. // discarded before returning to micropython.
let buffer_func = move || unsafe { unwrap!(get_buffer(data)) }; let buffer_func = move || unsafe { unwrap!(get_buffer(data)) };
let obj = LayoutObj::new(ConfirmHomescreen::new(title, buffer_func))?; let obj = LayoutObj::new(ConfirmHomescreen::new(title.into(), buffer_func))?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -470,7 +451,7 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M
.newline() .newline()
.text_normal(TR::backup__it_should_be_backed_up_now); .text_normal(TR::backup__it_should_be_backed_up_now);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success) Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success.into())
} }
1 => { 1 => {
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into()); let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into());
@ -478,8 +459,8 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M
let ops = let ops =
OpTextLayout::new(theme::TEXT_NORMAL).text_normal(TR::backup__recover_anytime); OpTextLayout::new(theme::TEXT_NORMAL).text_normal(TR::backup__recover_anytime);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
.with_title(tr_title_backup_wallet) .with_title(tr_title_backup_wallet.into())
} }
_ => unreachable!(), _ => unreachable!(),
}; };
@ -624,7 +605,7 @@ extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs
} }
ops = ops.text_mono(address); ops = ops.text_mono(address);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted).with_title(address_title) Page::new(btn_layout, btn_actions, formatted).with_title(address_title.into())
}; };
let pages = FlowPages::new(get_page, 1); let pages = FlowPages::new(get_page, 1);
@ -646,7 +627,7 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs:
let btn_actions = ButtonActions::cancel_none_confirm(); let btn_actions = ButtonActions::cancel_none_confirm();
let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount); let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title) Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.into())
}; };
let pages = FlowPages::new(get_page, 1); let pages = FlowPages::new(get_page, 1);
@ -686,7 +667,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
.text_mono(fee_amount); .text_mono(fee_amount);
let formatted = FormattedText::new(ops); let formatted = FormattedText::new(ops);
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
} }
1 => { 1 => {
// Fee rate info // Fee rate info
@ -771,7 +752,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
.text_mono(fee_value); .text_mono(fee_value);
let formatted = FormattedText::new(ops); let formatted = FormattedText::new(ops);
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title) Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.into())
} }
1 => { 1 => {
// Other information // Other information
@ -829,7 +810,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
}; };
let ops = OpTextLayout::new(style).text_mono(address); let ops = OpTextLayout::new(style).text_mono(address);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted).with_title(title) Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
}; };
let pages = FlowPages::new(get_page, 1); let pages = FlowPages::new(get_page, 1);
@ -846,10 +827,10 @@ fn tutorial_screen(
text: TR, text: TR,
btn_layout: ButtonLayout, btn_layout: ButtonLayout,
btn_actions: ButtonActions, btn_actions: ButtonActions,
) -> Page<StrBuffer> { ) -> Page {
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text); let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::new(btn_layout, btn_actions, formatted).with_title(title) Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
} }
extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
@ -1021,11 +1002,11 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text); let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
}; };
let pages = FlowPages::new(get_page, page_count); let pages = FlowPages::new(get_page, page_count);
let obj = LayoutObj::new(Flow::new(pages).with_common_title(title))?; let obj = LayoutObj::new(Flow::new(pages).with_common_title(title.into()))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1080,14 +1061,14 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
.text_bold(account); .text_bold(account);
let formatted = FormattedText::new(ops); let formatted = FormattedText::new(ops);
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
}; };
let pages = FlowPages::new(get_page, page_count); let pages = FlowPages::new(get_page, page_count);
// Returning the page index in case of confirmation. // Returning the page index in case of confirmation.
let obj = LayoutObj::new( let obj = LayoutObj::new(
Flow::new(pages) Flow::new(pages)
.with_common_title(title) .with_common_title(title.into())
.with_return_confirmed_index(), .with_return_confirmed_index(),
)?; )?;
Ok(obj.into()) Ok(obj.into())
@ -1115,7 +1096,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
ops = ops.text_normal(description); ops = ops.text_normal(description);
} }
let formatted = FormattedText::new(ops).vertically_centered(); let formatted = FormattedText::new(ops).vertically_centered();
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
}; };
let pages = FlowPages::new(get_page, 1); let pages = FlowPages::new(get_page, 1);
let obj = LayoutObj::new(Flow::new(pages))?; let obj = LayoutObj::new(Flow::new(pages))?;
@ -1132,7 +1113,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?; let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
let content = Frame::new( let content = Frame::new(
title, title.into(),
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]), Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
); );
let obj = if time_ms == 0 { let obj = if time_ms == 0 {
@ -1189,7 +1170,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
.newline() .newline()
.text_bold(TR::addr_mismatch__support_url); .text_bold(TR::addr_mismatch__support_url);
let formatted = FormattedText::new(ops); let formatted = FormattedText::new(ops);
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) Page::new(btn_layout, btn_actions, formatted)
}; };
let pages = FlowPages::new(get_page, 1); let pages = FlowPages::new(get_page, 1);
@ -1222,7 +1203,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
} }
let obj = LayoutObj::new(Frame::new( let obj = LayoutObj::new(Frame::new(
title, title.into(),
ShowMore::<Paragraphs<ParagraphVecShort>>::new( ShowMore::<Paragraphs<ParagraphVecShort>>::new(
paragraphs.into_paragraphs(), paragraphs.into_paragraphs(),
verb_cancel, verb_cancel,
@ -1291,7 +1272,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?; let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
let obj = LayoutObj::new(PinEntry::new(prompt, subprompt))?; let obj = LayoutObj::new(PinEntry::new(prompt.into(), subprompt.into()))?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -1302,7 +1283,9 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
let block = |_args: &[Obj], kwargs: &Map| { let block = |_args: &[Obj], kwargs: &Map| {
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
let obj = LayoutObj::new(Frame::new(prompt, PassphraseEntry::new()).with_title_centered())?; let obj = LayoutObj::new(
Frame::new(prompt.into(), PassphraseEntry::new()).with_title_centered(),
)?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1316,7 +1299,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::new( Frame::new(
prompt, prompt.into(),
WordlistEntry::prefilled_word( WordlistEntry::prefilled_word(
prefill_word.as_ref(), prefill_word.as_ref(),
WordlistType::Bip39, WordlistType::Bip39,
@ -1338,7 +1321,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::new( Frame::new(
prompt, prompt.into(),
WordlistEntry::prefilled_word( WordlistEntry::prefilled_word(
prefill_word.as_ref(), prefill_word.as_ref(),
WordlistType::Slip39, WordlistType::Slip39,
@ -1363,7 +1346,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
// Returning the index of the selected word, not the word itself // Returning the index of the selected word, not the word itself
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::new( Frame::new(
description, description.into(),
SimpleChoice::new(words, false) SimpleChoice::new(words, false)
.with_show_incomplete() .with_show_incomplete()
.with_return_index(), .with_return_index(),
@ -1402,7 +1385,8 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?; let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?;
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(), Frame::new(title.into(), NumberInput::new(min_count, max_count, count))
.with_title_centered(),
)?; )?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -1497,7 +1481,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
.collect(); .collect();
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::new(title, SimpleChoice::new(choices, false)).with_title_centered(), Frame::new(title.into(), SimpleChoice::new(choices, false)).with_title_centered(),
)?; )?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -1542,7 +1526,9 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
// Description updates are received as &str and we need to provide a way to // Description updates are received as &str and we need to provide a way to
// convert them to StrBuffer. // convert them to StrBuffer.
let obj = LayoutObj::new(Progress::new(indeterminate, description).with_title(title))?; let obj = LayoutObj::new(
Progress::new(indeterminate, description.into()).with_title(title.into()),
)?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1583,9 +1569,13 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?; let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?; let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?;
let notification = notification.map(|w| (w, notification_level)); let notification = notification.map(|w| (w.into(), notification_level));
let loader_description = hold.then_some("Locking the device...".into()); let loader_description = hold.then_some("Locking the device...".into());
let obj = LayoutObj::new(Homescreen::new(label, notification, loader_description))?; let obj = LayoutObj::new(Homescreen::new(
label.into(),
notification,
loader_description,
))?;
if skip_first_paint { if skip_first_paint {
obj.skip_first_paint(); obj.skip_first_paint();
} }
@ -1604,7 +1594,11 @@ extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut
let coinjoin_authorized: bool = kwargs.get_or(Qstr::MP_QSTR_coinjoin_authorized, false)?; let coinjoin_authorized: bool = kwargs.get_or(Qstr::MP_QSTR_coinjoin_authorized, false)?;
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?; let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
let obj = LayoutObj::new(Lockscreen::new(label, bootscreen, coinjoin_authorized)?)?; let obj = LayoutObj::new(Lockscreen::new(
label.into(),
bootscreen,
coinjoin_authorized,
))?;
if skip_first_paint { if skip_first_paint {
obj.skip_first_paint(); obj.skip_first_paint();
} }
@ -1624,16 +1618,27 @@ extern "C" fn new_confirm_firmware_update(
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?; let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
let title = TR::firmware_update__title; let title = TR::firmware_update__title;
let message = Label::left_aligned(description, theme::TEXT_NORMAL).vertically_centered(); let message =
Label::left_aligned(description.into(), theme::TEXT_NORMAL).vertically_centered();
let fingerprint = Label::left_aligned( let fingerprint = Label::left_aligned(
fingerprint, fingerprint.into(),
theme::TEXT_NORMAL.with_line_breaking(LineBreaking::BreakWordsNoHyphen), theme::TEXT_NORMAL.with_line_breaking(LineBreaking::BreakWordsNoHyphen),
) )
.vertically_centered(); .vertically_centered();
let obj = LayoutObj::new( let obj = LayoutObj::new(
Confirm::new(theme::BG, title, message, None, TR::buttons__install, false) Confirm::new(
.with_info_screen(TR::firmware_update__title_fingerprint, fingerprint), theme::BG,
title.into(),
message,
None,
TR::buttons__install.as_tstring(),
false,
)
.with_info_screen(
TR::firmware_update__title_fingerprint.as_tstring(),
fingerprint.into(),
),
)?; )?;
Ok(obj.into()) Ok(obj.into())
}; };

@ -6,10 +6,12 @@ use crate::ui::{
use super::{component::ErrorScreen, constant}; use super::{component::ErrorScreen, constant};
#[cfg(not(feature = "micropython"))]
use crate::strutil::TString;
#[cfg(not(feature = "micropython"))] #[cfg(not(feature = "micropython"))]
// SAFETY: Actually safe but see below // SAFETY: Actually safe but see below
unsafe fn get_str(text: &str) -> &str { unsafe fn get_str(text: &str) -> TString {
text TString::Str(text)
} }
#[cfg(feature = "micropython")] #[cfg(feature = "micropython")]
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not // SAFETY: The caller is responsible for ensuring that the StrBuffer does not
@ -25,7 +27,7 @@ pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) {
let msg = unsafe { get_str(msg) }; let msg = unsafe { get_str(msg) };
let footer = unsafe { get_str(footer) }; let footer = unsafe { get_str(footer) };
let mut frame = ErrorScreen::new(title, msg, footer); let mut frame = ErrorScreen::new(title.into(), msg.into(), footer.into());
frame.place(constant::screen()); frame.place(constant::screen());
frame.paint(); frame.paint();
} }

@ -1,14 +1,17 @@
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx, Label, Pad}, strutil::TString,
constant::screen, ui::{
display::Icon, component::{Child, Component, Event, EventCtx, Label, Pad},
geometry::{Alignment, Insets, Point, Rect}, constant::screen,
model_tt::{ display::Icon,
component::{Button, ButtonMsg::Clicked}, geometry::{Alignment, Insets, Point, Rect},
constant::WIDTH, model_tt::{
theme::bootloader::{ component::{Button, ButtonMsg::Clicked},
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT, constant::WIDTH,
CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_WARNING, TITLE_AREA, theme::bootloader::{
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT,
CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_WARNING, TITLE_AREA,
},
}, },
}, },
}; };
@ -22,15 +25,15 @@ pub enum IntroMsg {
pub struct Intro<'a> { pub struct Intro<'a> {
bg: Pad, bg: Pad,
title: Child<Label<&'a str>>, title: Child<Label<'a>>,
menu: Child<Button<&'static str>>, menu: Child<Button>,
host: Child<Button<&'static str>>, host: Child<Button>,
text: Child<Label<&'a str>>, text: Child<Label<'a>>,
warn: Option<Child<Label<&'a str>>>, warn: Option<Child<Label<'a>>>,
} }
impl<'a> Intro<'a> { impl<'a> Intro<'a> {
pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self { pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
Self { Self {
bg: Pad::with_background(BLD_BG).with_clear(), bg: Pad::with_background(BLD_BG).with_clear(),
title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()), title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()),
@ -39,10 +42,10 @@ impl<'a> Intro<'a> {
.styled(button_bld_menu()) .styled(button_bld_menu())
.with_expanded_touch_area(Insets::uniform(13)), .with_expanded_touch_area(Insets::uniform(13)),
), ),
host: Child::new(Button::with_text("INSTALL FIRMWARE").styled(button_bld())), host: Child::new(Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld())),
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()), text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
warn: (!fw_ok).then_some(Child::new( warn: (!fw_ok).then_some(Child::new(
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_WARNING) Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
.vertically_centered(), .vertically_centered(),
)), )),
} }

@ -29,10 +29,10 @@ pub enum MenuMsg {
pub struct Menu { pub struct Menu {
bg: Pad, bg: Pad,
title: Child<Label<&'static str>>, title: Child<Label<'static>>,
close: Child<Button<&'static str>>, close: Child<Button>,
reboot: Child<Button<&'static str>>, reboot: Child<Button>,
reset: Child<Button<&'static str>>, reset: Child<Button>,
} }
impl Menu { impl Menu {
@ -43,7 +43,7 @@ impl Menu {
let mut instance = Self { let mut instance = Self {
bg: Pad::with_background(BLD_BG), bg: Pad::with_background(BLD_BG),
title: Child::new( title: Child::new(
Label::left_aligned("BOOTLOADER", text_title(BLD_BG)).vertically_centered(), Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG)).vertically_centered(),
), ),
close: Child::new( close: Child::new(
Button::with_icon(Icon::new(X32)) Button::with_icon(Icon::new(X32))

@ -77,14 +77,16 @@ extern "C" fn screen_install_confirm(
} else { } else {
"DOWNGRADE FW" "DOWNGRADE FW"
}; };
let title = Label::left_aligned(title_str, TEXT_BOLD).vertically_centered(); let title = Label::left_aligned(title_str.into(), TEXT_BOLD).vertically_centered();
let msg = Label::left_aligned(version_str.as_ref(), TEXT_NORMAL); let msg = Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL);
let alert = let alert = (!should_keep_seed).then_some(Label::left_aligned(
(!should_keep_seed).then_some(Label::left_aligned("SEED WILL BE ERASED!", TEXT_BOLD)); "SEED WILL BE ERASED!".into(),
TEXT_BOLD,
));
let (left, right) = if should_keep_seed { let (left, right) = if should_keep_seed {
let l = Button::with_text("CANCEL").styled(button_bld()); let l = Button::with_text("CANCEL".into()).styled(button_bld());
let r = Button::with_text("INSTALL").styled(button_confirm()); let r = Button::with_text("INSTALL".into()).styled(button_confirm());
(l, r) (l, r)
} else { } else {
let l = Button::with_icon(Icon::new(X24)).styled(button_bld()); let l = Button::with_icon(Icon::new(X24)).styled(button_bld());
@ -93,8 +95,8 @@ extern "C" fn screen_install_confirm(
}; };
let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg).with_info( let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg).with_info(
"FW FINGERPRINT", "FW FINGERPRINT".into(),
fingerprint_str, fingerprint_str.into(),
button_bld_menu(), button_bld_menu(),
); );
@ -110,13 +112,13 @@ extern "C" fn screen_wipe_confirm() -> u32 {
let icon = Icon::new(FIRE40); let icon = Icon::new(FIRE40);
let msg = Label::centered( let msg = Label::centered(
"Are you sure you want to factory reset the device?", "Are you sure you want to factory reset the device?".into(),
TEXT_WIPE_NORMAL, TEXT_WIPE_NORMAL,
); );
let alert = Label::centered("SEED AND FIRMWARE\nWILL BE ERASED!", TEXT_WIPE_BOLD); let alert = Label::centered("SEED AND FIRMWARE\nWILL BE ERASED!".into(), TEXT_WIPE_BOLD);
let right = Button::with_text("RESET").styled(button_wipe_confirm()); let right = Button::with_text("RESET".into()).styled(button_wipe_confirm());
let left = Button::with_text("CANCEL").styled(button_wipe_cancel()); let left = Button::with_text("CANCEL".into()).styled(button_wipe_cancel());
let mut frame = let mut frame =
Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg).with_alert(alert); Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg).with_alert(alert);
@ -151,7 +153,11 @@ extern "C" fn screen_intro(
unwrap!(version_str.push_str("\nby ")); unwrap!(version_str.push_str("\nby "));
unwrap!(version_str.push_str(vendor)); unwrap!(version_str.push_str(vendor));
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok); let mut frame = Intro::new(
title_str.as_str().into(),
version_str.as_str().into(),
fw_ok,
);
run(&mut frame) run(&mut frame)
} }
@ -222,8 +228,8 @@ extern "C" fn screen_wipe_success() {
let mut frame = ResultScreen::new( let mut frame = ResultScreen::new(
&RESULT_WIPE, &RESULT_WIPE,
Icon::new(CHECK40), Icon::new(CHECK40),
"Trezor reset\nsuccessfully", "Trezor reset\nsuccessfully".into(),
Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(), Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style()).vertically_centered(),
true, true,
); );
show(&mut frame, true); show(&mut frame, true);
@ -234,8 +240,8 @@ extern "C" fn screen_wipe_fail() {
let mut frame = ResultScreen::new( let mut frame = ResultScreen::new(
&RESULT_WIPE, &RESULT_WIPE,
Icon::new(WARNING40), Icon::new(WARNING40),
"Trezor reset was\nnot successful", "Trezor reset was\nnot successful".into(),
Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(), Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style()).vertically_centered(),
true, true,
); );
show(&mut frame, true); show(&mut frame, true);
@ -265,8 +271,9 @@ extern "C" fn screen_install_fail() {
let mut frame = ResultScreen::new( let mut frame = ResultScreen::new(
&RESULT_FW_INSTALL, &RESULT_FW_INSTALL,
Icon::new(WARNING40), Icon::new(WARNING40),
"Firmware installation was not successful", "Firmware installation was not successful".into(),
Label::centered(RECONNECT_MESSAGE, RESULT_FW_INSTALL.title_style()).vertically_centered(), Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style())
.vertically_centered(),
true, true,
); );
show(&mut frame, true); show(&mut frame, true);
@ -276,8 +283,8 @@ fn screen_install_success_bld(msg: &str, complete_draw: bool) {
let mut frame = ResultScreen::new( let mut frame = ResultScreen::new(
&RESULT_FW_INSTALL, &RESULT_FW_INSTALL,
Icon::new(CHECK40), Icon::new(CHECK40),
"Firmware installed\nsuccessfully", "Firmware installed\nsuccessfully".into(),
Label::centered(msg, RESULT_FW_INSTALL.title_style()).vertically_centered(), Label::centered(msg.into(), RESULT_FW_INSTALL.title_style()).vertically_centered(),
complete_draw, complete_draw,
); );
show(&mut frame, complete_draw); show(&mut frame, complete_draw);
@ -287,8 +294,8 @@ fn screen_install_success_initial(msg: &str, complete_draw: bool) {
let mut frame = ResultScreen::new( let mut frame = ResultScreen::new(
&RESULT_INITIAL, &RESULT_INITIAL,
Icon::new(CHECK40), Icon::new(CHECK40),
"Firmware installed\nsuccessfully", "Firmware installed\nsuccessfully".into(),
Label::centered(msg, RESULT_INITIAL.title_style()).vertically_centered(), Label::centered(msg.into(), RESULT_INITIAL.title_style()).vertically_centered(),
complete_draw, complete_draw,
); );
show(&mut frame, complete_draw); show(&mut frame, complete_draw);

@ -19,9 +19,9 @@ use super::{theme, Frame, FrameMsg};
const MAX_XPUBS: usize = 16; const MAX_XPUBS: usize = 16;
pub struct AddressDetails<T> { pub struct AddressDetails<T> {
qr_code: Frame<Qr, T>, qr_code: Frame<Qr>,
details: Frame<Paragraphs<ParagraphVecShort<'static>>, T>, details: Frame<Paragraphs<ParagraphVecShort<'static>>>,
xpub_view: Frame<Paragraphs<Paragraph<'static>>, T>, xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
xpubs: Vec<(T, T), MAX_XPUBS>, xpubs: Vec<(T, T), MAX_XPUBS>,
xpub_page_count: Vec<u8, MAX_XPUBS>, xpub_page_count: Vec<u8, MAX_XPUBS>,
current_page: usize, current_page: usize,
@ -60,14 +60,14 @@ where
let result = Self { let result = Self {
qr_code: Frame::left_aligned( qr_code: Frame::left_aligned(
theme::label_title(), theme::label_title(),
qr_title, qr_title.into(),
Qr::new(qr_address, case_sensitive)?.with_border(7), Qr::new(qr_address, case_sensitive)?.with_border(7),
) )
.with_cancel_button() .with_cancel_button()
.with_border(theme::borders_horizontal_scroll()), .with_border(theme::borders_horizontal_scroll()),
details: Frame::left_aligned( details: Frame::left_aligned(
theme::label_title(), theme::label_title(),
details_title, details_title.into(),
para.into_paragraphs(), para.into_paragraphs(),
) )
.with_cancel_button() .with_cancel_button()
@ -101,7 +101,7 @@ where
// repaint after page change so we can use a dummy context here. // repaint after page change so we can use a dummy context here.
let mut dummy_ctx = EventCtx::new(); let mut dummy_ctx = EventCtx::new();
self.xpub_view self.xpub_view
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone()); .update_title(&mut dummy_ctx, self.xpubs[i].0.clone().into());
self.xpub_view.update_content(&mut dummy_ctx, |p| { self.xpub_view.update_content(&mut dummy_ctx, |p| {
p.inner_mut().update(self.xpubs[i].1.clone()); p.inner_mut().update(self.xpubs[i].1.clone());
let npages = p.page_count(); let npages = p.page_count();

@ -1,18 +1,22 @@
use crate::ui::{ use crate::{
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad}, strutil::TString,
constant, ui::{
constant::screen, component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
display::{Color, Icon}, constant,
geometry::{Alignment2D, Insets, Offset, Point, Rect}, constant::screen,
model_tt::{ display::{Color, Icon},
component::{Button, ButtonMsg::Clicked, ButtonStyleSheet}, geometry::{Alignment2D, Insets, Offset, Point, Rect},
constant::WIDTH, model_tt::{
theme::{ component::{Button, ButtonMsg::Clicked, ButtonStyleSheet},
bootloader::{ constant::WIDTH,
text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING, theme::{
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32, TITLE_AREA, X32, bootloader::{
text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT,
CONTENT_PADDING, CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32,
TITLE_AREA, X32,
},
WHITE,
}, },
WHITE,
}, },
}, },
}; };
@ -31,41 +35,38 @@ pub enum ConfirmMsg {
Confirm = 2, Confirm = 2,
} }
pub enum ConfirmTitle<T> { pub enum ConfirmTitle {
Text(Label<T>), Text(Label<'static>),
Icon(Icon), Icon(Icon),
} }
pub struct ConfirmInfo<T> { pub struct ConfirmInfo<'a> {
pub title: Child<Label<T>>, pub title: Child<Label<'a>>,
pub text: Child<Label<T>>, pub text: Child<Label<'a>>,
pub info_button: Child<Button<&'static str>>, pub info_button: Child<Button>,
pub close_button: Child<Button<&'static str>>, pub close_button: Child<Button>,
} }
pub struct Confirm<T> { pub struct Confirm<'a> {
bg: Pad, bg: Pad,
content_pad: Pad, content_pad: Pad,
bg_color: Color, bg_color: Color,
title: ConfirmTitle<T>, title: ConfirmTitle,
message: Child<Label<T>>, message: Child<Label<'a>>,
alert: Option<Child<Label<T>>>, alert: Option<Child<Label<'static>>>,
left_button: Child<Button<T>>, left_button: Child<Button>,
right_button: Child<Button<T>>, right_button: Child<Button>,
info: Option<ConfirmInfo<T>>, info: Option<ConfirmInfo<'a>>,
show_info: bool, show_info: bool,
} }
impl<T> Confirm<T> impl<'a> Confirm<'a> {
where
T: AsRef<str>,
{
pub fn new( pub fn new(
bg_color: Color, bg_color: Color,
left_button: Button<T>, left_button: Button,
right_button: Button<T>, right_button: Button,
title: ConfirmTitle<T>, title: ConfirmTitle,
message: Label<T>, message: Label<'a>,
) -> Self { ) -> Self {
Self { Self {
bg: Pad::with_background(bg_color).with_clear(), bg: Pad::with_background(bg_color).with_clear(),
@ -81,12 +82,17 @@ where
} }
} }
pub fn with_alert(mut self, alert: Label<T>) -> Self { pub fn with_alert(mut self, alert: Label<'static>) -> Self {
self.alert = Some(Child::new(alert.vertically_centered())); self.alert = Some(Child::new(alert.vertically_centered()));
self self
} }
pub fn with_info(mut self, title: T, text: T, menu_button: ButtonStyleSheet) -> Self { pub fn with_info(
mut self,
title: TString<'a>,
text: TString<'a>,
menu_button: ButtonStyleSheet,
) -> Self {
self.info = Some(ConfirmInfo { self.info = Some(ConfirmInfo {
title: Child::new( title: Child::new(
Label::left_aligned(title, text_title(self.bg_color)).vertically_centered(), Label::left_aligned(title, text_title(self.bg_color)).vertically_centered(),
@ -109,10 +115,7 @@ where
} }
} }
impl<T> Component for Confirm<T> impl Component for Confirm<'_> {
where
T: AsRef<str>,
{
type Msg = ConfirmMsg; type Msg = ConfirmMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -242,10 +245,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Confirm<T> impl crate::trace::Trace for Confirm<'_> {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("BlConfirm"); t.component("BlConfirm");
} }

@ -1,6 +1,7 @@
#[cfg(feature = "haptic")] #[cfg(feature = "haptic")]
use crate::trezorhal::haptic::{play, HapticEffect}; use crate::trezorhal::haptic::{play, HapticEffect};
use crate::{ use crate::{
strutil::TString,
time::Duration, time::Duration,
ui::{ ui::{
component::{ component::{
@ -21,22 +22,22 @@ pub enum ButtonMsg {
LongPressed, LongPressed,
} }
pub struct Button<T> { pub struct Button {
area: Rect, area: Rect,
touch_expand: Option<Insets>, touch_expand: Option<Insets>,
content: ButtonContent<T>, content: ButtonContent,
styles: ButtonStyleSheet, styles: ButtonStyleSheet,
state: State, state: State,
long_press: Option<Duration>, long_press: Option<Duration>,
long_timer: Option<TimerToken>, long_timer: Option<TimerToken>,
} }
impl<T> Button<T> { impl Button {
/// Offsets the baseline of the button text either up (negative) or down /// Offsets the baseline of the button text either up (negative) or down
/// (positive). /// (positive).
pub const BASELINE_OFFSET: i16 = -2; pub const BASELINE_OFFSET: i16 = -2;
pub const fn new(content: ButtonContent<T>) -> Self { pub const fn new(content: ButtonContent) -> Self {
Self { Self {
content, content,
area: Rect::zero(), area: Rect::zero(),
@ -48,7 +49,7 @@ impl<T> Button<T> {
} }
} }
pub const fn with_text(text: T) -> Self { pub const fn with_text(text: TString<'static>) -> Self {
Self::new(ButtonContent::Text(text)) Self::new(ButtonContent::Text(text))
} }
@ -117,17 +118,14 @@ impl<T> Button<T> {
matches!(self.state, State::Disabled) matches!(self.state, State::Disabled)
} }
pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent<T>) pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent) {
where
T: PartialEq,
{
if self.content != content { if self.content != content {
self.content = content; self.content = content;
ctx.request_paint(); ctx.request_paint();
} }
} }
pub fn content(&self) -> &ButtonContent<T> { pub fn content(&self) -> &ButtonContent {
&self.content &self.content
} }
@ -189,22 +187,19 @@ impl<T> Button<T> {
} }
} }
pub fn paint_content(&self, style: &ButtonStyle) pub fn paint_content(&self, style: &ButtonStyle) {
where
T: AsRef<str>,
{
match &self.content { match &self.content {
ButtonContent::Empty => {} ButtonContent::Empty => {}
ButtonContent::Text(text) => { ButtonContent::Text(text) => {
let text = text.as_ref(); let text = text;
let width = style.font.text_width(text); let width = style.font.text_width(text.map(|c| c));
let height = style.font.text_height(); let height = style.font.text_height();
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_left( display::text_left(
start_of_baseline, start_of_baseline,
text, text.into(),
style.font, style.font,
style.text_color, style.text_color,
style.button_color, style.button_color,
@ -231,10 +226,7 @@ impl<T> Button<T> {
} }
} }
impl<T> Component for Button<T> impl Component for Button {
where
T: AsRef<str>,
{
type Msg = ButtonMsg; type Msg = ButtonMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -327,15 +319,12 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Button<T> impl crate::trace::Trace for Button {
where
T: AsRef<str>,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Button"); t.component("Button");
match &self.content { match &self.content {
ButtonContent::Empty => {} ButtonContent::Empty => {}
ButtonContent::Text(text) => t.string("text", text.as_ref().into()), ButtonContent::Text(text) => t.string("text", *text),
ButtonContent::Icon(_) => t.bool("icon", true), ButtonContent::Icon(_) => t.bool("icon", true),
ButtonContent::IconAndText(content) => { ButtonContent::IconAndText(content) => {
t.string("text", content.text.into()); t.string("text", content.text.into());
@ -355,9 +344,9 @@ enum State {
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum ButtonContent<T> { pub enum ButtonContent {
Empty, Empty,
Text(T), Text(TString<'static>),
Icon(Icon), Icon(Icon),
IconAndText(IconText), IconAndText(IconText),
IconBlend(Icon, Icon, Offset), IconBlend(Icon, Icon, Offset),
@ -381,19 +370,15 @@ pub struct ButtonStyle {
pub border_width: i16, pub border_width: i16,
} }
impl<T> Button<T> { impl Button {
pub fn cancel_confirm( pub fn cancel_confirm(
left: Button<T>, left: Button,
right: Button<T>, right: Button,
left_is_small: bool, left_is_small: bool,
) -> CancelConfirm< ) -> CancelConfirm<
T,
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
> > {
where
T: AsRef<str>,
{
let width = if left_is_small { let width = if left_is_small {
theme::BUTTON_WIDTH theme::BUTTON_WIDTH
} else { } else {
@ -412,21 +397,17 @@ impl<T> Button<T> {
} }
pub fn cancel_confirm_text( pub fn cancel_confirm_text(
left: Option<T>, left: Option<TString<'static>>,
right: Option<T>, right: Option<TString<'static>>,
) -> CancelConfirm< ) -> CancelConfirm<
T,
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
> > {
where
T: AsRef<str>,
{
let left_is_small: bool; let left_is_small: bool;
let left = if let Some(verb) = left { let left = if let Some(verb) = left {
left_is_small = verb.as_ref().len() <= 4; left_is_small = verb.len() <= 4;
if verb.as_ref() == "^" { if verb == "^".into() {
Button::with_icon(theme::ICON_UP) Button::with_icon(theme::ICON_UP)
} else { } else {
Button::with_text(verb) Button::with_text(verb)
@ -444,17 +425,13 @@ impl<T> Button<T> {
} }
pub fn cancel_info_confirm( pub fn cancel_info_confirm(
confirm: T, confirm: TString<'static>,
info: T, info: TString<'static>,
) -> CancelInfoConfirm< ) -> CancelInfoConfirm<
T,
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
> > {
where
T: AsRef<str>,
{
let right = Button::with_text(confirm) let right = Button::with_text(confirm)
.styled(theme::button_confirm()) .styled(theme::button_confirm())
.map(|msg| { .map(|msg| {
@ -479,16 +456,12 @@ impl<T> Button<T> {
} }
pub fn select_word( pub fn select_word(
words: [T; 3], words: [TString<'static>; 3],
) -> CancelInfoConfirm< ) -> CancelInfoConfirm<
T,
impl Fn(ButtonMsg) -> Option<SelectWordMsg>, impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
impl Fn(ButtonMsg) -> Option<SelectWordMsg>, impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
impl Fn(ButtonMsg) -> Option<SelectWordMsg>, impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
> > {
where
T: AsRef<str>,
{
let btn = move |i, word| { let btn = move |i, word| {
Button::with_text(word) Button::with_text(word)
.styled(theme::button_pin()) .styled(theme::button_pin())
@ -521,11 +494,10 @@ pub enum CancelConfirmMsg {
Confirmed, Confirmed,
} }
type CancelInfoConfirm<T, F0, F1, F2> = FixedHeightBar< type CancelInfoConfirm<F0, F1, F2> =
Split<MsgMap<Button<T>, F0>, Split<MsgMap<Button<T>, F1>, MsgMap<Button<T>, F2>>>, FixedHeightBar<Split<MsgMap<Button, F0>, Split<MsgMap<Button, F1>, MsgMap<Button, F2>>>>;
>;
type CancelConfirm<T, F0, F1> = FixedHeightBar<Split<MsgMap<Button<T>, F0>, MsgMap<Button<T>, F1>>>; type CancelConfirm<F0, F1> = FixedHeightBar<Split<MsgMap<Button, F0>, MsgMap<Button, F1>>>;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum CancelInfoConfirmMsg { pub enum CancelInfoConfirmMsg {

@ -3,7 +3,7 @@ use core::mem;
use crate::{ use crate::{
error::Error, error::Error,
maybe_trace::MaybeTrace, maybe_trace::MaybeTrace,
micropython::buffer::StrBuffer, strutil::TString,
translations::TR, translations::TR,
ui::{ ui::{
component::{ component::{
@ -25,29 +25,23 @@ const LOADER_INNER: i16 = 28;
const LOADER_OFFSET: i16 = -34; const LOADER_OFFSET: i16 = -34;
const LOADER_SPEED: u16 = 5; const LOADER_SPEED: u16 = 5;
pub struct CoinJoinProgress<T, U> { pub struct CoinJoinProgress<U> {
value: u16, value: u16,
indeterminate: bool, indeterminate: bool,
content: Child<Frame<Split<Empty, U>, StrBuffer>>, content: Child<Frame<Split<Empty, U>>>,
// Label is not a child since circular loader paints large black rectangle which overlaps it. // Label is not a child since circular loader paints large black rectangle which overlaps it.
// To work around this, draw label every time loader is drawn. // To work around this, draw label every time loader is drawn.
label: Label<T>, label: Label<'static>,
} }
impl<T, U> CoinJoinProgress<T, U> impl<U> CoinJoinProgress<U> {
where
T: AsRef<str>,
{
pub fn new( pub fn new(
text: T, text: TString<'static>,
indeterminate: bool, indeterminate: bool,
) -> Result<CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>, Error> ) -> Result<CoinJoinProgress<impl Component<Msg = Never> + MaybeTrace>, Error> {
where
T: AsRef<str>,
{
let style = theme::label_coinjoin_progress(); let style = theme::label_coinjoin_progress();
let label = Label::centered( let label = Label::centered(
TryInto::<StrBuffer>::try_into(TR::coinjoin__title_do_not_disconnect)?, TryInto::<TString>::try_into(TR::coinjoin__title_do_not_disconnect)?,
style, style,
) )
.vertically_centered(); .vertically_centered();
@ -57,12 +51,15 @@ where
} }
} }
impl<T, U> CoinJoinProgress<T, U> impl<U> CoinJoinProgress<U>
where where
T: AsRef<str>,
U: Component<Msg = Never>, U: Component<Msg = Never>,
{ {
pub fn with_background(text: T, inner: U, indeterminate: bool) -> Result<Self, Error> { pub fn with_background(
text: TString<'static>,
inner: U,
indeterminate: bool,
) -> Result<Self, Error> {
Ok(Self { Ok(Self {
value: 0, value: 0,
indeterminate, indeterminate,
@ -77,9 +74,8 @@ where
} }
} }
impl<T, U> Component for CoinJoinProgress<T, U> impl<U> Component for CoinJoinProgress<U>
where where
T: AsRef<str>,
U: Component<Msg = Never>, U: Component<Msg = Never>,
{ {
type Msg = Never; type Msg = Never;
@ -132,9 +128,8 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for CoinJoinProgress<T, U> impl<U> crate::trace::Trace for CoinJoinProgress<U>
where where
T: AsRef<str>,
U: Component + crate::trace::Trace, U: Component + crate::trace::Trace,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {

@ -1,7 +1,10 @@
use crate::ui::{ use crate::{
component::{Child, Component, Event, EventCtx, Label, Never, Pad}, strutil::TString,
constant::screen, ui::{
geometry::{Alignment2D, Point, Rect}, component::{Child, Component, Event, EventCtx, Label, Never, Pad},
constant::screen,
geometry::{Alignment2D, Point, Rect},
},
}; };
use crate::ui::model_tt::{ use crate::ui::model_tt::{
@ -19,15 +22,15 @@ const STYLE: &ResultStyle = &crate::ui::model_tt::theme::bootloader::RESULT_WIPE
#[cfg(not(feature = "bootloader"))] #[cfg(not(feature = "bootloader"))]
const STYLE: &ResultStyle = &super::theme::RESULT_ERROR; const STYLE: &ResultStyle = &super::theme::RESULT_ERROR;
pub struct ErrorScreen<'a, T> { pub struct ErrorScreen<'a> {
bg: Pad, bg: Pad,
title: Child<Label<T>>, title: Child<Label<'a>>,
message: Child<Label<T>>, message: Child<Label<'a>>,
footer: Child<ResultFooter<'a, T>>, footer: Child<ResultFooter<'a>>,
} }
impl<T: AsRef<str>> ErrorScreen<'_, T> { impl<'a> ErrorScreen<'a> {
pub fn new(title: T, message: T, footer: T) -> Self { pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self {
let title = Label::centered(title, STYLE.title_style()); let title = Label::centered(title, STYLE.title_style());
let message = Label::centered(message, STYLE.message_style()); let message = Label::centered(message, STYLE.message_style());
let footer = ResultFooter::new( let footer = ResultFooter::new(
@ -44,7 +47,7 @@ impl<T: AsRef<str>> ErrorScreen<'_, T> {
} }
} }
impl<T: AsRef<str>> Component for ErrorScreen<'_, T> { impl<'a> Component for ErrorScreen<'a> {
type Msg = Never; type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect { fn place(&mut self, _bounds: Rect) -> Rect {
@ -62,7 +65,7 @@ impl<T: AsRef<str>> Component for ErrorScreen<'_, T> {
); );
self.message.place(message_area); self.message.place(message_area);
let (_, bottom_area) = ResultFooter::<T>::split_bounds(); let (_, bottom_area) = ResultFooter::<'a>::split_bounds();
self.footer.place(bottom_area); self.footer.place(bottom_area);
screen() screen()

@ -1,11 +1,14 @@
use crate::ui::{ use crate::{
component::{image::Image, Child, Component, Event, EventCtx, Label}, strutil::TString,
display, ui::{
geometry::{Insets, Rect}, component::{image::Image, Child, Component, Event, EventCtx, Label},
model_tt::component::{ display,
fido_icons::get_fido_icon_data, geometry::{Insets, Rect},
swipe::{Swipe, SwipeDirection}, model_tt::component::{
theme, ScrollBar, fido_icons::get_fido_icon_data,
swipe::{Swipe, SwipeDirection},
theme, ScrollBar,
},
}, },
}; };
@ -22,10 +25,10 @@ pub enum FidoMsg {
Cancelled, Cancelled,
} }
pub struct FidoConfirm<F: Fn(usize) -> T, T, U> { pub struct FidoConfirm<F: Fn(usize) -> TString<'static>, U> {
page_swipe: Swipe, page_swipe: Swipe,
app_name: Label<T>, app_name: Label<'static>,
account_name: Label<T>, account_name: Label<'static>,
icon: Child<Image>, icon: Child<Image>,
/// Function/closure that will return appropriate page on demand. /// Function/closure that will return appropriate page on demand.
get_account: F, get_account: F,
@ -34,20 +37,19 @@ pub struct FidoConfirm<F: Fn(usize) -> T, T, U> {
controls: U, controls: U,
} }
impl<F, T, U> FidoConfirm<F, T, U> impl<F, U> FidoConfirm<F, U>
where where
F: Fn(usize) -> T, F: Fn(usize) -> TString<'static>,
T: AsRef<str> + From<&'static str>,
U: Component<Msg = CancelConfirmMsg>, U: Component<Msg = CancelConfirmMsg>,
{ {
pub fn new( pub fn new(
app_name: T, app_name: TString<'static>,
get_account: F, get_account: F,
page_count: usize, page_count: usize,
icon_name: Option<T>, icon_name: Option<TString<'static>>,
controls: U, controls: U,
) -> Self { ) -> Self {
let icon_data = get_fido_icon_data(icon_name.as_ref()); let icon_data = get_fido_icon_data(icon_name);
// Preparing scrollbar and setting its page-count. // Preparing scrollbar and setting its page-count.
let mut scrollbar = ScrollBar::horizontal(); let mut scrollbar = ScrollBar::horizontal();
@ -99,10 +101,9 @@ where
} }
} }
impl<F, T, U> Component for FidoConfirm<F, T, U> impl<F, U> Component for FidoConfirm<F, U>
where where
F: Fn(usize) -> T, F: Fn(usize) -> TString<'static>,
T: AsRef<str> + From<&'static str>,
U: Component<Msg = CancelConfirmMsg>, U: Component<Msg = CancelConfirmMsg>,
{ {
type Msg = FidoMsg; type Msg = FidoMsg;
@ -177,8 +178,8 @@ where
// Account name is optional. // Account name is optional.
// Showing it only if it differs from app name. // Showing it only if it differs from app name.
// (Dummy requests usually have some text as both app_name and account_name.) // (Dummy requests usually have some text as both app_name and account_name.)
if !current_account.as_ref().is_empty() if !current_account.map(|c| c).is_empty()
&& current_account.as_ref() != self.app_name.text().as_ref() && current_account.map(|c| c) != self.app_name.text().map(|c| c)
{ {
self.account_name.set_text(current_account); self.account_name.set_text(current_account);
self.account_name.paint(); self.account_name.paint();
@ -200,9 +201,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<F, T, U> crate::trace::Trace for FidoConfirm<F, T, U> impl<F, T> crate::trace::Trace for FidoConfirm<F, T>
where where
F: Fn(usize) -> T, F: Fn(usize) -> TString<'static>,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("FidoConfirm"); t.component("FidoConfirm");

@ -3,6 +3,9 @@
//! do not edit manually! //! do not edit manually!
use crate::strutil::TString;
const ICON_APPLE: &[u8] = include_res!("model_tt/res/fido/icon_apple.toif"); const ICON_APPLE: &[u8] = include_res!("model_tt/res/fido/icon_apple.toif");
const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif"); const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif");
const ICON_BINANCE: &[u8] = include_res!("model_tt/res/fido/icon_binance.toif"); const ICON_BINANCE: &[u8] = include_res!("model_tt/res/fido/icon_binance.toif");
@ -39,9 +42,9 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"
/// Translates icon name into its data. /// Translates icon name into its data.
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not /// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
/// supplied. /// supplied.
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] { pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
if let Some(icon_name) = icon_name { if let Some(icon_name) = icon_name {
match icon_name.as_ref() { match icon_name.map(|c| c) {
"apple" => ICON_APPLE, "apple" => ICON_APPLE,
"aws" => ICON_AWS, "aws" => ICON_AWS,
"binance" => ICON_BINANCE, "binance" => ICON_BINANCE,

@ -2,6 +2,9 @@
//! (by running `make templates` in `core`) //! (by running `make templates` in `core`)
//! do not edit manually! //! do not edit manually!
use crate::strutil::TString;
<% <%
icons: list[tuple[str, str]] = [] icons: list[tuple[str, str]] = []
for app in fido: for app in fido:
@ -21,9 +24,9 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"
/// Translates icon name into its data. /// Translates icon name into its data.
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not /// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
/// supplied. /// supplied.
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] { pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
if let Some(icon_name) = icon_name { if let Some(icon_name) = icon_name {
match icon_name.as_ref() { match icon_name.map(|c| c) {
% for icon_name, var_name in icons: % for icon_name, var_name in icons:
"${icon_name}" => ICON_${var_name}, "${icon_name}" => ICON_${var_name},
% endfor % endfor

@ -1,18 +1,21 @@
use super::theme; use super::theme;
use crate::ui::{ use crate::{
component::{ strutil::TString,
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx, ui::{
component::{
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx,
},
display::Icon,
geometry::{Alignment, Insets, Offset, Rect},
model_tt::component::{Button, ButtonMsg, CancelInfoConfirmMsg},
}, },
display::Icon,
geometry::{Alignment, Insets, Offset, Rect},
model_tt::component::{Button, ButtonMsg, CancelInfoConfirmMsg},
}; };
pub struct Frame<T, U> { pub struct Frame<T> {
border: Insets, border: Insets,
title: Child<Label<U>>, title: Child<Label<'static>>,
subtitle: Option<Child<Label<U>>>, subtitle: Option<Child<Label<'static>>>,
button: Option<Child<Button<&'static str>>>, button: Option<Child<Button>>,
button_msg: CancelInfoConfirmMsg, button_msg: CancelInfoConfirmMsg,
content: Child<T>, content: Child<T>,
} }
@ -22,12 +25,16 @@ pub enum FrameMsg<T> {
Button(CancelInfoConfirmMsg), Button(CancelInfoConfirmMsg),
} }
impl<T, U> Frame<T, U> impl<T> Frame<T>
where where
T: Component, T: Component,
U: AsRef<str>,
{ {
pub fn new(style: TextStyle, alignment: Alignment, title: U, content: T) -> Self { pub fn new(
style: TextStyle,
alignment: Alignment,
title: TString<'static>,
content: T,
) -> Self {
Self { Self {
title: Child::new(Label::new(title, alignment, style)), title: Child::new(Label::new(title, alignment, style)),
subtitle: None, subtitle: None,
@ -38,15 +45,15 @@ where
} }
} }
pub fn left_aligned(style: TextStyle, title: U, content: T) -> Self { pub fn left_aligned(style: TextStyle, title: TString<'static>, content: T) -> Self {
Self::new(style, Alignment::Start, title, content) Self::new(style, Alignment::Start, title, content)
} }
pub fn right_aligned(style: TextStyle, title: U, content: T) -> Self { pub fn right_aligned(style: TextStyle, title: TString<'static>, content: T) -> Self {
Self::new(style, Alignment::End, title, content) Self::new(style, Alignment::End, title, content)
} }
pub fn centered(style: TextStyle, title: U, content: T) -> Self { pub fn centered(style: TextStyle, title: TString<'static>, content: T) -> Self {
Self::new(style, Alignment::Center, title, content) Self::new(style, Alignment::Center, title, content)
} }
@ -55,7 +62,7 @@ where
self self
} }
pub fn with_subtitle(mut self, style: TextStyle, subtitle: U) -> Self { pub fn with_subtitle(mut self, style: TextStyle, subtitle: TString<'static>) -> Self {
self.subtitle = Some(Child::new(Label::new( self.subtitle = Some(Child::new(Label::new(
subtitle, subtitle,
self.title.inner().alignment(), self.title.inner().alignment(),
@ -91,7 +98,7 @@ where
self.content.inner() self.content.inner()
} }
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: U) { pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: TString<'static>) {
self.title.mutate(ctx, |ctx, t| { self.title.mutate(ctx, |ctx, t| {
t.set_text(new_title); t.set_text(new_title);
t.request_complete_repaint(ctx) t.request_complete_repaint(ctx)
@ -110,10 +117,9 @@ where
} }
} }
impl<T, U> Component for Frame<T, U> impl<T> Component for Frame<T>
where where
T: Component, T: Component,
U: AsRef<str>,
{ {
type Msg = FrameMsg<T::Msg>; type Msg = FrameMsg<T::Msg>;
@ -179,10 +185,9 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for Frame<T, U> impl<T> crate::trace::Trace for Frame<T>
where where
T: crate::trace::Trace, T: crate::trace::Trace,
U: AsRef<str>,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Frame"); t.component("Frame");

@ -269,14 +269,14 @@ impl crate::trace::Trace for Homescreen {
} }
} }
pub struct Lockscreen { pub struct Lockscreen<'a> {
label: TString<'static>, label: TString<'a>,
bootscreen: bool, bootscreen: bool,
coinjoin_authorized: bool, coinjoin_authorized: bool,
} }
impl Lockscreen { impl<'a> Lockscreen<'a> {
pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self { pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
Lockscreen { Lockscreen {
label, label,
bootscreen, bootscreen,
@ -285,7 +285,7 @@ impl Lockscreen {
} }
} }
impl Component for Lockscreen { impl Component for Lockscreen<'_> {
type Msg = HomescreenMsg; type Msg = HomescreenMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -401,7 +401,7 @@ fn is_image_toif(buffer: &[u8]) -> bool {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl crate::trace::Trace for Lockscreen { impl crate::trace::Trace for Lockscreen<'_> {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Lockscreen"); t.component("Lockscreen");
} }

@ -21,9 +21,9 @@ use heapless::String;
const MAX_LENGTH: usize = 8; const MAX_LENGTH: usize = 8;
pub struct Bip39Input { pub struct Bip39Input {
button: Button<&'static str>, button: Button,
// used only to keep track of suggestion text color // used only to keep track of suggestion text color
button_suggestion: Button<&'static str>, button_suggestion: Button,
textbox: TextBox<MAX_LENGTH>, textbox: TextBox<MAX_LENGTH>,
multi_tap: MultiTapKeyboard, multi_tap: MultiTapKeyboard,
options_num: Option<usize>, options_num: Option<usize>,
@ -263,7 +263,7 @@ impl Bip39Input {
// Disabled button. // Disabled button.
self.button.disable(ctx); self.button.disable(ctx);
self.button.set_stylesheet(ctx, theme::button_pin()); self.button.set_stylesheet(ctx, theme::button_pin());
self.button.set_content(ctx, ButtonContent::Text("")); self.button.set_content(ctx, ButtonContent::Text("".into()));
} }
} }
} }

@ -1,9 +1,12 @@
use crate::ui::{ use crate::{
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe}, strutil::TString,
geometry::{Alignment2D, Grid, Offset, Rect}, ui::{
model_tt::{ component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
component::{Button, ButtonMsg, Swipe, SwipeDirection}, geometry::{Alignment2D, Grid, Offset, Rect},
theme, model_tt::{
component::{Button, ButtonMsg, Swipe, SwipeDirection},
theme,
},
}, },
}; };
@ -14,27 +17,26 @@ pub enum MnemonicKeyboardMsg {
Previous, Previous,
} }
pub struct MnemonicKeyboard<T, U> { pub struct MnemonicKeyboard<T> {
/// Initial prompt, displayed on empty input. /// Initial prompt, displayed on empty input.
prompt: Child<Maybe<Label<U>>>, prompt: Child<Maybe<Label<'static>>>,
/// Backspace button. /// Backspace button.
back: Child<Maybe<Button<&'static str>>>, back: Child<Maybe<Button>>,
/// Input area, acting as the auto-complete and confirm button. /// Input area, acting as the auto-complete and confirm button.
input: Child<Maybe<T>>, input: Child<Maybe<T>>,
/// Key buttons. /// Key buttons.
keys: [Child<Button<&'static str>>; MNEMONIC_KEY_COUNT], keys: [Child<Button>; MNEMONIC_KEY_COUNT],
/// Swipe controller - allowing for going to the previous word. /// Swipe controller - allowing for going to the previous word.
swipe: Swipe, swipe: Swipe,
/// Whether going back is allowed (is not on the very first word). /// Whether going back is allowed (is not on the very first word).
can_go_back: bool, can_go_back: bool,
} }
impl<T, U> MnemonicKeyboard<T, U> impl<T> MnemonicKeyboard<T>
where where
T: MnemonicInput, T: MnemonicInput,
U: AsRef<str>,
{ {
pub fn new(input: T, prompt: U, can_go_back: bool) -> Self { pub fn new(input: T, prompt: TString<'static>, can_go_back: bool) -> Self {
// Input might be already pre-filled // Input might be already pre-filled
let prompt_visible = input.is_empty(); let prompt_visible = input.is_empty();
@ -57,7 +59,7 @@ where
)), )),
input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)), input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)),
keys: T::keys() keys: T::keys()
.map(|t| Button::with_text(t).styled(theme::button_pin())) .map(|t| Button::with_text(t.into()).styled(theme::button_pin()))
.map(Child::new), .map(Child::new),
swipe: Swipe::new().right(), swipe: Swipe::new().right(),
can_go_back, can_go_back,
@ -99,10 +101,9 @@ where
} }
} }
impl<T, U> Component for MnemonicKeyboard<T, U> impl<T> Component for MnemonicKeyboard<T>
where where
T: MnemonicInput, T: MnemonicInput,
U: AsRef<str>,
{ {
type Msg = MnemonicKeyboardMsg; type Msg = MnemonicKeyboardMsg;
@ -210,10 +211,9 @@ pub enum MnemonicInputMsg {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for MnemonicKeyboard<T, U> impl<T> crate::trace::Trace for MnemonicKeyboard<T>
where where
T: MnemonicInput + crate::trace::Trace, T: MnemonicInput + crate::trace::Trace,
U: AsRef<str>,
{ {
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("MnemonicKeyboard"); t.component("MnemonicKeyboard");

@ -1,16 +1,19 @@
use crate::ui::{ use crate::{
component::{ strutil::TString,
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never, ui::{
component::{
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
},
display,
geometry::{Grid, Offset, Rect},
model_tt::component::{
button::{Button, ButtonContent, ButtonMsg},
keyboard::common::{paint_pending_marker, MultiTapKeyboard},
swipe::{Swipe, SwipeDirection},
theme, ScrollBar,
},
util::long_line_content_with_ellipsis,
}, },
display,
geometry::{Grid, Offset, Rect},
model_tt::component::{
button::{Button, ButtonContent, ButtonMsg},
keyboard::common::{paint_pending_marker, MultiTapKeyboard},
swipe::{Swipe, SwipeDirection},
theme, ScrollBar,
},
util::long_line_content_with_ellipsis,
}; };
pub enum PassphraseKeyboardMsg { pub enum PassphraseKeyboardMsg {
@ -21,9 +24,9 @@ pub enum PassphraseKeyboardMsg {
pub struct PassphraseKeyboard { pub struct PassphraseKeyboard {
page_swipe: Swipe, page_swipe: Swipe,
input: Child<Input>, input: Child<Input>,
back: Child<Button<&'static str>>, back: Child<Button>,
confirm: Child<Button<&'static str>>, confirm: Child<Button>,
keys: [Child<Button<&'static str>>; KEY_COUNT], keys: [Child<Button>; KEY_COUNT],
scrollbar: ScrollBar, scrollbar: ScrollBar,
fade: bool, fade: bool,
} }
@ -67,20 +70,20 @@ impl PassphraseKeyboard {
} }
} }
fn key_text(content: &ButtonContent<&'static str>) -> &'static str { fn key_text(content: &ButtonContent) -> TString<'static> {
match content { match content {
ButtonContent::Text(text) => text, ButtonContent::Text(text) => *text,
ButtonContent::Icon(_) => " ", ButtonContent::Icon(_) => " ".into(),
ButtonContent::IconAndText(_) => " ", ButtonContent::IconAndText(_) => " ".into(),
ButtonContent::Empty => "", ButtonContent::Empty => "".into(),
ButtonContent::IconBlend(_, _, _) => "", ButtonContent::IconBlend(_, _, _) => "".into(),
} }
} }
fn key_content(text: &'static str) -> ButtonContent<&'static str> { fn key_content(text: &'static str) -> ButtonContent {
match text { match text {
" " => ButtonContent::Icon(theme::ICON_SPACE), " " => ButtonContent::Icon(theme::ICON_SPACE),
t => ButtonContent::Text(t), t => ButtonContent::Text(t.into()),
} }
} }
@ -270,7 +273,7 @@ impl Component for PassphraseKeyboard {
// character in textbox. If not, let's just append the first character. // character in textbox. If not, let's just append the first character.
let text = Self::key_text(btn.inner().content()); let text = Self::key_text(btn.inner().content());
self.input.mutate(ctx, |ctx, i| { self.input.mutate(ctx, |ctx, i| {
let edit = i.multi_tap.click_key(ctx, key, text); let edit = i.multi_tap.click_key(ctx, key, text.map(|c| c));
i.textbox.apply(ctx, edit); i.textbox.apply(ctx, edit);
}); });
self.after_edit(ctx); self.after_edit(ctx);

@ -2,6 +2,7 @@ use core::mem;
use heapless::String; use heapless::String;
use crate::{ use crate::{
strutil::TString,
time::Duration, time::Duration,
trezorhal::random, trezorhal::random,
ui::{ ui::{
@ -39,32 +40,29 @@ const HEADER_PADDING: Insets = Insets::new(
HEADER_PADDING_SIDE, HEADER_PADDING_SIDE,
); );
pub struct PinKeyboard<T> { pub struct PinKeyboard<'a> {
allow_cancel: bool, allow_cancel: bool,
major_prompt: Child<Label<T>>, major_prompt: Child<Label<'a>>,
minor_prompt: Child<Label<T>>, minor_prompt: Child<Label<'a>>,
major_warning: Option<Child<Label<T>>>, major_warning: Option<Child<Label<'a>>>,
textbox: Child<PinDots>, textbox: Child<PinDots>,
textbox_pad: Pad, textbox_pad: Pad,
erase_btn: Child<Maybe<Button<&'static str>>>, erase_btn: Child<Maybe<Button>>,
cancel_btn: Child<Maybe<Button<&'static str>>>, cancel_btn: Child<Maybe<Button>>,
confirm_btn: Child<Button<&'static str>>, confirm_btn: Child<Button>,
digit_btns: [Child<Button<&'static str>>; DIGIT_COUNT], digit_btns: [Child<Button>; DIGIT_COUNT],
warning_timer: Option<TimerToken>, warning_timer: Option<TimerToken>,
} }
impl<T> PinKeyboard<T> impl<'a> PinKeyboard<'a> {
where
T: AsRef<str>,
{
// Label position fine-tuning. // Label position fine-tuning.
const MAJOR_OFF: Offset = Offset::y(11); const MAJOR_OFF: Offset = Offset::y(11);
const MINOR_OFF: Offset = Offset::y(11); const MINOR_OFF: Offset = Offset::y(11);
pub fn new( pub fn new(
major_prompt: T, major_prompt: TString<'a>,
minor_prompt: T, minor_prompt: TString<'a>,
major_warning: Option<T>, major_warning: Option<TString<'a>>,
allow_cancel: bool, allow_cancel: bool,
) -> Self { ) -> Self {
// Control buttons. // Control buttons.
@ -102,12 +100,12 @@ where
} }
} }
fn generate_digit_buttons() -> [Child<Button<&'static str>>; DIGIT_COUNT] { fn generate_digit_buttons() -> [Child<Button>; DIGIT_COUNT] {
// Generate a random sequence of digits from 0 to 9. // Generate a random sequence of digits from 0 to 9.
let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
random::shuffle(&mut digits); random::shuffle(&mut digits);
digits digits
.map(Button::with_text) .map(|c| Button::with_text(c.into()))
.map(|b| b.styled(theme::button_pin())) .map(|b| b.styled(theme::button_pin()))
.map(Child::new) .map(Child::new)
} }
@ -146,10 +144,7 @@ where
} }
} }
impl<T> Component for PinKeyboard<T> impl Component for PinKeyboard<'_> {
where
T: AsRef<str>,
{
type Msg = PinKeyboardMsg; type Msg = PinKeyboardMsg;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -238,7 +233,7 @@ where
for btn in &mut self.digit_btns { for btn in &mut self.digit_btns {
if let Some(Clicked) = btn.event(ctx, event) { if let Some(Clicked) = btn.event(ctx, event) {
if let ButtonContent::Text(text) = btn.inner().content() { if let ButtonContent::Text(text) = btn.inner().content() {
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text)); self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text.into()));
self.pin_modified(ctx); self.pin_modified(ctx);
return None; return None;
} }
@ -460,10 +455,7 @@ impl Component for PinDots {
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for PinKeyboard<T> impl crate::trace::Trace for PinKeyboard<'_> {
where
T: AsRef<str>,
{
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 // So that debuglink knows the locations of the buttons
@ -471,7 +463,7 @@ where
for btn in self.digit_btns.iter() { for btn in self.digit_btns.iter() {
let btn_content = btn.inner().content(); let btn_content = btn.inner().content();
if let ButtonContent::Text(text) = btn_content { if let ButtonContent::Text(text) = btn_content {
unwrap!(digits_order.push_str(text)); unwrap!(digits_order.push_str(text.into()));
} }
} }
t.string("digits_order", digits_order.as_str().into()); t.string("digits_order", digits_order.as_str().into());

@ -28,7 +28,7 @@ use crate::{
const MAX_LENGTH: usize = 8; const MAX_LENGTH: usize = 8;
pub struct Slip39Input { pub struct Slip39Input {
button: Button<&'static str>, button: Button,
textbox: TextBox<MAX_LENGTH>, textbox: TextBox<MAX_LENGTH>,
multi_tap: MultiTapKeyboard, multi_tap: MultiTapKeyboard,
final_word: Option<&'static str>, final_word: Option<&'static str>,
@ -293,7 +293,7 @@ impl Slip39Input {
} else { } else {
// Disabled button. // Disabled button.
self.button.disable(ctx); self.button.disable(ctx);
self.button.set_content(ctx, ButtonContent::Text("")); self.button.set_content(ctx, ButtonContent::Text("".into()));
} }
} }

@ -12,7 +12,7 @@ const LABELS: [&str; 5] = ["12", "18", "20", "24", "33"];
const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)]; const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)];
pub struct SelectWordCount { pub struct SelectWordCount {
button: [Button<&'static str>; NUMBERS.len()], button: [Button; NUMBERS.len()],
} }
pub enum SelectWordCountMsg { pub enum SelectWordCountMsg {
@ -22,7 +22,7 @@ pub enum SelectWordCountMsg {
impl SelectWordCount { impl SelectWordCount {
pub fn new() -> Self { pub fn new() -> Self {
SelectWordCount { SelectWordCount {
button: LABELS.map(|t| Button::with_text(t).styled(theme::button_pin())), button: LABELS.map(|t| Button::with_text(t.into()).styled(theme::button_pin())),
} }
} }
} }

@ -1,6 +1,5 @@
use crate::{ use crate::{
error::Error, error::Error,
micropython::buffer::StrBuffer,
strutil::{self, StringType}, strutil::{self, StringType},
translations::TR, translations::TR,
ui::{ ui::{
@ -31,8 +30,8 @@ where
input: Child<NumberInput>, input: Child<NumberInput>,
paragraphs: Child<Paragraphs<Paragraph<'static>>>, paragraphs: Child<Paragraphs<Paragraph<'static>>>,
paragraphs_pad: Pad, paragraphs_pad: Pad,
info_button: Child<Button<StrBuffer>>, info_button: Child<Button>,
confirm_button: Child<Button<StrBuffer>>, confirm_button: Child<Button>,
} }
impl<T, F> NumberInputDialog<T, F> impl<T, F> NumberInputDialog<T, F>
@ -154,8 +153,8 @@ pub enum NumberInputMsg {
pub struct NumberInput { pub struct NumberInput {
area: Rect, area: Rect,
dec: Child<Button<&'static str>>, dec: Child<Button>,
inc: Child<Button<&'static str>>, inc: Child<Button>,
min: u32, min: u32,
max: u32, max: u32,
value: u32, value: u32,
@ -163,10 +162,10 @@ pub struct NumberInput {
impl NumberInput { impl NumberInput {
pub fn new(min: u32, max: u32, value: u32) -> Self { pub fn new(min: u32, max: u32, value: u32) -> Self {
let dec = Button::with_text("-") let dec = Button::with_text("-".into())
.styled(theme::button_counter()) .styled(theme::button_counter())
.into_child(); .into_child();
let inc = Button::with_text("+") let inc = Button::with_text("+".into())
.styled(theme::button_counter()) .styled(theme::button_counter())
.into_child(); .into_child();
let value = value.clamp(min, max); let value = value.clamp(min, max);
@ -217,7 +216,7 @@ impl Component for NumberInput {
let mut buf = [0u8; 10]; let mut buf = [0u8; 10];
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) { if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
let digit_font = Font::DEMIBOLD; let digit_font = Font::DEMIBOLD;
let y_offset = digit_font.text_height() / 2 + Button::<&str>::BASELINE_OFFSET; let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET;
display::rect_fill(self.area, theme::BG); display::rect_fill(self.area, theme::BG);
display::text_center( display::text_center(
self.area.center() + Offset::y(y_offset), self.area.center() + Offset::y(y_offset),

@ -1,6 +1,6 @@
use crate::{ use crate::{
error::Error, error::Error,
micropython::buffer::StrBuffer, strutil::TString,
time::Instant, time::Instant,
translations::TR, translations::TR,
ui::{ ui::{
@ -19,7 +19,7 @@ use super::{
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel /// Allows pagination of inner component. Shows scroll bar, confirm & cancel
/// buttons. Optionally handles hold-to-confirm with loader. /// buttons. Optionally handles hold-to-confirm with loader.
pub struct ButtonPage<T, U> { pub struct ButtonPage<T> {
/// Inner component. /// Inner component.
content: T, content: T,
/// Cleared when page changes. /// Cleared when page changes.
@ -29,10 +29,10 @@ pub struct ButtonPage<T, U> {
scrollbar: ScrollBar, scrollbar: ScrollBar,
/// Hold-to-confirm mode whenever this is `Some(loader)`. /// Hold-to-confirm mode whenever this is `Some(loader)`.
loader: Option<Loader>, loader: Option<Loader>,
button_cancel: Option<Button<U>>, button_cancel: Option<Button>,
button_confirm: Button<U>, button_confirm: Button,
button_prev: Button<&'static str>, button_prev: Button,
button_next: Button<&'static str>, button_next: Button,
/// Show cancel button instead of back button. /// Show cancel button instead of back button.
cancel_from_any_page: bool, cancel_from_any_page: bool,
/// Whether to pass-through left swipe to parent component. /// Whether to pass-through left swipe to parent component.
@ -43,7 +43,7 @@ pub struct ButtonPage<T, U> {
fade: Option<u16>, fade: Option<u16>,
} }
impl<T> ButtonPage<T, StrBuffer> impl<T> ButtonPage<T>
where where
T: Paginate, T: Paginate,
T: Component, T: Component,
@ -56,11 +56,10 @@ where
} }
} }
impl<T, U> ButtonPage<T, U> impl<T> ButtonPage<T>
where where
T: Paginate, T: Paginate,
T: Component, T: Component,
U: AsRef<str> + From<&'static str>,
{ {
pub fn new(content: T, background: Color) -> Self { pub fn new(content: T, background: Color) -> Self {
Self { Self {
@ -85,11 +84,15 @@ where
self self
} }
pub fn with_cancel_confirm(mut self, left: Option<U>, right: Option<U>) -> Self { pub fn with_cancel_confirm(
mut self,
left: Option<TString<'static>>,
right: Option<TString<'static>>,
) -> Self {
let cancel = match left { let cancel = match left {
Some(verb) => match verb.as_ref() { Some(verb) => match verb {
"^" => Button::with_icon(theme::ICON_UP), TString::Str("^") => Button::with_icon(theme::ICON_UP),
"<" => Button::with_icon(theme::ICON_BACK), TString::Str("<") => Button::with_icon(theme::ICON_BACK),
_ => Button::with_text(verb), _ => Button::with_text(verb),
}, },
_ => Button::with_icon(theme::ICON_CANCEL), _ => Button::with_icon(theme::ICON_CANCEL),
@ -282,11 +285,10 @@ enum HandleResult<T> {
Continue, Continue,
} }
impl<T, U> Component for ButtonPage<T, U> impl<T> Component for ButtonPage<T>
where where
T: Paginate, T: Paginate,
T: Component, T: Component,
U: AsRef<str> + From<&'static str>,
{ {
type Msg = PageMsg<T::Msg>; type Msg = PageMsg<T::Msg>;
@ -294,7 +296,7 @@ where
let small_left_button = match (&self.button_cancel, &self.button_confirm) { let small_left_button = match (&self.button_cancel, &self.button_confirm) {
(None, _) => true, (None, _) => true,
(Some(cancel), confirm) => match (cancel.content(), confirm.content()) { (Some(cancel), confirm) => match (cancel.content(), confirm.content()) {
(ButtonContent::Text(t), _) => t.as_ref().len() <= 4, (ButtonContent::Text(t), _) => t.len() <= 4,
(ButtonContent::Icon(_), ButtonContent::Icon(_)) => false, (ButtonContent::Icon(_), ButtonContent::Icon(_)) => false,
_ => true, _ => true,
}, },
@ -425,7 +427,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for ButtonPage<T, U> impl<T> crate::trace::Trace for ButtonPage<T>
where where
T: crate::trace::Trace, T: crate::trace::Trace,
{ {
@ -528,10 +530,8 @@ mod tests {
#[test] #[test]
fn paragraphs_empty() { fn paragraphs_empty() {
let mut page = ButtonPage::<_, &'static str>::new( let mut page =
Paragraphs::<[Paragraph<'static>; 0]>::new([]), ButtonPage::<_>::new(Paragraphs::<[Paragraph<'static>; 0]>::new([]), theme::BG);
theme::BG,
);
page.place(SCREEN); page.place(SCREEN);
let expected = serde_json::json!({ let expected = serde_json::json!({
@ -554,7 +554,7 @@ mod tests {
#[test] #[test]
fn paragraphs_single() { fn paragraphs_single() {
let mut page = ButtonPage::<_, &'static str>::new( let mut page = ButtonPage::<_>::new(
Paragraphs::new([ Paragraphs::new([
Paragraph::new( Paragraph::new(
&theme::TEXT_NORMAL, &theme::TEXT_NORMAL,
@ -592,7 +592,7 @@ mod tests {
#[test] #[test]
fn paragraphs_one_long() { fn paragraphs_one_long() {
let mut page = ButtonPage::<_, &'static str>::new( let mut page = ButtonPage::<_>::new(
Paragraphs::new( Paragraphs::new(
Paragraph::new( Paragraph::new(
&theme::TEXT_BOLD, &theme::TEXT_BOLD,
@ -650,7 +650,7 @@ mod tests {
#[test] #[test]
fn paragraphs_three_long() { fn paragraphs_three_long() {
let mut page = ButtonPage::<_, &'static str>::new( let mut page = ButtonPage::<_>::new(
Paragraphs::new([ Paragraphs::new([
Paragraph::new( Paragraph::new(
&theme::TEXT_BOLD, &theme::TEXT_BOLD,
@ -750,7 +750,7 @@ mod tests {
#[test] #[test]
fn paragraphs_hard_break() { fn paragraphs_hard_break() {
let mut page = ButtonPage::<_, &'static str>::new( let mut page = ButtonPage::<_>::new(
Paragraphs::new([ Paragraphs::new([
Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(), Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(),
Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(), Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(),

@ -1,7 +1,7 @@
use core::mem; use core::mem;
use crate::{ use crate::{
strutil::{StringType, TString}, strutil::TString,
ui::{ ui::{
component::{ component::{
base::ComponentExt, base::ComponentExt,
@ -18,8 +18,8 @@ use crate::{
use super::theme; use super::theme;
pub struct Progress<T> { pub struct Progress {
title: Child<Label<T>>, title: Child<Label<'static>>,
value: u16, value: u16,
loader_y_offset: i16, loader_y_offset: i16,
indeterminate: bool, indeterminate: bool,
@ -27,13 +27,14 @@ pub struct Progress<T> {
description_pad: Pad, description_pad: Pad,
} }
impl<T> Progress<T> impl Progress {
where
T: StringType,
{
const AREA: Rect = constant::screen().inset(theme::borders()); const AREA: Rect = constant::screen().inset(theme::borders());
pub fn new(title: T, indeterminate: bool, description: TString<'static>) -> Self { pub fn new(
title: TString<'static>,
indeterminate: bool,
description: TString<'static>,
) -> Self {
Self { Self {
title: Label::centered(title, theme::label_progress()).into_child(), title: Label::centered(title, theme::label_progress()).into_child(),
value: 0, value: 0,
@ -48,10 +49,7 @@ where
} }
} }
impl<T> Component for Progress<T> impl Component for Progress {
where
T: StringType,
{
type Msg = Never; type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect { fn place(&mut self, _bounds: Rect) -> Rect {
@ -117,10 +115,7 @@ where
} }
#[cfg(feature = "ui_debug")] #[cfg(feature = "ui_debug")]
impl<T> crate::trace::Trace for Progress<T> impl crate::trace::Trace for Progress {
where
T: StringType,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) { fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("Progress"); t.component("Progress");
} }

@ -1,5 +1,5 @@
use crate::{ use crate::{
strutil::StringType, strutil::TString,
ui::{ ui::{
component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad}, component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad},
constant::screen, constant::screen,
@ -41,14 +41,14 @@ impl ResultStyle {
} }
} }
pub struct ResultFooter<'a, T> { pub struct ResultFooter<'a> {
style: &'a ResultStyle, style: &'a ResultStyle,
text: Label<T>, text: Label<'a>,
area: Rect, area: Rect,
} }
impl<'a, T: AsRef<str>> ResultFooter<'a, T> { impl<'a> ResultFooter<'a> {
pub fn new(text: Label<T>, style: &'a ResultStyle) -> Self { pub fn new(text: Label<'a>, style: &'a ResultStyle) -> Self {
Self { Self {
style, style,
text, text,
@ -69,7 +69,7 @@ impl<'a, T: AsRef<str>> ResultFooter<'a, T> {
} }
} }
impl<T: AsRef<str>> Component for ResultFooter<'_, T> { impl Component for ResultFooter<'_> {
type Msg = Never; type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect { fn place(&mut self, bounds: Rect) -> Rect {
@ -78,6 +78,10 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
bounds bounds
} }
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) { fn paint(&mut self) {
// divider line // divider line
let bar = Rect::from_center_and_size( let bar = Rect::from_center_and_size(
@ -89,27 +93,23 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
// footer text // footer text
self.text.paint(); self.text.paint();
} }
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
} }
pub struct ResultScreen<'a, T> { pub struct ResultScreen<'a> {
bg: Pad, bg: Pad,
footer_pad: Pad, footer_pad: Pad,
style: &'a ResultStyle, style: &'a ResultStyle,
icon: Icon, icon: Icon,
message: Child<Label<T>>, message: Child<Label<'a>>,
footer: Child<ResultFooter<'a, &'a str>>, footer: Child<ResultFooter<'a>>,
} }
impl<'a, T: StringType> ResultScreen<'a, T> { impl<'a> ResultScreen<'a> {
pub fn new( pub fn new(
style: &'a ResultStyle, style: &'a ResultStyle,
icon: Icon, icon: Icon,
message: T, message: TString<'a>,
footer: Label<&'a str>, footer: Label<'a>,
complete_draw: bool, complete_draw: bool,
) -> Self { ) -> Self {
let mut instance = Self { let mut instance = Self {
@ -130,13 +130,13 @@ impl<'a, T: StringType> ResultScreen<'a, T> {
} }
} }
impl<'a, T: StringType> Component for ResultScreen<'a, T> { impl<'a> Component for ResultScreen<'a> {
type Msg = Never; type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect { fn place(&mut self, _bounds: Rect) -> Rect {
self.bg.place(screen()); self.bg.place(screen());
let (main_area, footer_area) = ResultFooter::<&'a str>::split_bounds(); let (main_area, footer_area) = ResultFooter::<'a>::split_bounds();
self.footer_pad.place(footer_area); self.footer_pad.place(footer_area);
self.footer.place(footer_area); self.footer.place(footer_area);

@ -100,10 +100,9 @@ impl TryFrom<SelectWordCountMsg> for Obj {
} }
} }
impl<F, T, U> ComponentMsgObj for FidoConfirm<F, T, U> impl<F, U> ComponentMsgObj for FidoConfirm<F, U>
where where
F: Fn(usize) -> T, F: Fn(usize) -> TString<'static>,
T: StringType,
U: Component<Msg = CancelConfirmMsg>, U: Component<Msg = CancelConfirmMsg>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
@ -141,10 +140,7 @@ where
} }
} }
impl<T> ComponentMsgObj for PinKeyboard<T> impl ComponentMsgObj for PinKeyboard<'_> {
where
T: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
PinKeyboardMsg::Confirmed => self.pin().try_into(), PinKeyboardMsg::Confirmed => self.pin().try_into(),
@ -162,10 +158,9 @@ impl ComponentMsgObj for PassphraseKeyboard {
} }
} }
impl<T, U> ComponentMsgObj for MnemonicKeyboard<T, U> impl<T> ComponentMsgObj for MnemonicKeyboard<T>
where where
T: MnemonicInput, T: MnemonicInput,
U: AsRef<str>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -181,10 +176,9 @@ where
} }
} }
impl<T, U> ComponentMsgObj for Frame<T, U> impl<T> ComponentMsgObj for Frame<T>
where where
T: ComponentMsgObj, T: ComponentMsgObj,
U: AsRef<str>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -194,10 +188,9 @@ where
} }
} }
impl<T, U> ComponentMsgObj for ButtonPage<T, U> impl<T> ComponentMsgObj for ButtonPage<T>
where where
T: Component + Paginate, T: Component + Paginate,
U: AsRef<str> + From<&'static str>,
{ {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
@ -269,10 +262,7 @@ where
} }
} }
impl<T> ComponentMsgObj for Progress<T> impl ComponentMsgObj for Progress {
where
T: StringType,
{
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
unreachable!() unreachable!()
} }
@ -286,7 +276,7 @@ impl ComponentMsgObj for Homescreen {
} }
} }
impl ComponentMsgObj for Lockscreen { impl ComponentMsgObj for Lockscreen<'_> {
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()), HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
@ -342,9 +332,8 @@ where
} }
} }
impl<T, U> ComponentMsgObj for CoinJoinProgress<T, U> impl<U> ComponentMsgObj for CoinJoinProgress<U>
where where
T: AsRef<str>,
U: Component<Msg = Never>, U: Component<Msg = Never>,
{ {
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
@ -352,10 +341,7 @@ where
} }
} }
impl<T> ComponentMsgObj for super::component::bl_confirm::Confirm<T> impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
where
T: AsRef<str>,
{
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
match msg { match msg {
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()), super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
@ -401,12 +387,17 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
let mut page = if hold { let mut page = if hold {
ButtonPage::new(paragraphs, theme::BG).with_hold()? ButtonPage::new(paragraphs, theme::BG).with_hold()?
} else { } else {
ButtonPage::new(paragraphs, theme::BG).with_cancel_confirm(verb_cancel, verb) ButtonPage::new(paragraphs, theme::BG)
.with_cancel_confirm(verb_cancel.map(|c| c.into()), verb.map(|c| c.into()))
}; };
if hold && hold_danger { if hold && hold_danger {
page = page.with_confirm_style(theme::button_danger()) page = page.with_confirm_style(theme::button_danger())
} }
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?; let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title.into(),
page,
))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -438,9 +429,9 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
ButtonPage::new(FormattedText::new(ops).vertically_centered(), theme::BG) ButtonPage::new(FormattedText::new(ops).vertically_centered(), theme::BG)
.with_cancel_confirm(None, verb), .with_cancel_confirm(None, verb.map(|v| v.into())),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -530,14 +521,14 @@ impl ConfirmBlobParams {
let mut page = ButtonPage::new(paragraphs, theme::BG); let mut page = ButtonPage::new(paragraphs, theme::BG);
if let Some(verb) = self.verb { if let Some(verb) = self.verb {
page = page.with_cancel_confirm(self.verb_cancel, Some(verb)) page = page.with_cancel_confirm(self.verb_cancel.map(|v| v.into()), Some(verb.into()))
} }
if self.hold { if self.hold {
page = page.with_hold()? page = page.with_hold()?
} }
let mut frame = Frame::left_aligned(theme::label_title(), self.title, page); let mut frame = Frame::left_aligned(theme::label_title(), self.title.into(), page);
if let Some(subtitle) = self.subtitle { if let Some(subtitle) = self.subtitle {
frame = frame.with_subtitle(theme::label_subtitle(), subtitle); frame = frame.with_subtitle(theme::label_subtitle(), subtitle.into());
} }
if self.info_button { if self.info_button {
frame = frame.with_info_button(); frame = frame.with_info_button();
@ -604,10 +595,10 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::left_aligned( Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
ButtonPage::new(paragraphs, theme::BG) ButtonPage::new(paragraphs, theme::BG)
.with_swipe_left() .with_swipe_left()
.with_cancel_confirm(None, Some(verb)), .with_cancel_confirm(None, Some(verb.into())),
) )
.with_info_button(), .with_info_button(),
)?; )?;
@ -628,13 +619,17 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
&theme::TEXT_MONO, &theme::TEXT_MONO,
&theme::TEXT_MONO, &theme::TEXT_MONO,
)?; )?;
let page: ButtonPage<_, StrBuffer> = if hold { let page: ButtonPage<_> = if hold {
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()? ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?
} else { } else {
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
.with_cancel_confirm(None, Some(TR::buttons__confirm.try_into()?)) .with_cancel_confirm(None, Some(TR::buttons__confirm.try_into()?))
}; };
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?; let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(),
title.into(),
page,
))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -666,10 +661,10 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
}; };
let tr_change: StrBuffer = TR::buttons__change.try_into()?; let tr_change: StrBuffer = TR::buttons__change.try_into()?;
let buttons = Button::cancel_confirm_text(None, Some(tr_change)); let buttons = Button::cancel_confirm_text(None, Some(tr_change.into()));
let obj = LayoutObj::new(Frame::centered( let obj = LayoutObj::new(Frame::centered(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons), Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -691,12 +686,12 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
let paragraphs = Paragraphs::new(par_array); let paragraphs = Paragraphs::new(par_array);
let buttons = Button::cancel_confirm( let buttons = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL), Button::with_icon(theme::ICON_CANCEL),
Button::with_text(button).styled(theme::button_confirm()), Button::with_text(button.into()).styled(theme::button_confirm()),
true, true,
); );
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new(paragraphs, buttons), Dialog::new(paragraphs, buttons),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -768,7 +763,7 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::left_aligned( Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
SimplePage::new(paragraphs.into_paragraphs(), axis, theme::BG) SimplePage::new(paragraphs.into_paragraphs(), axis, theme::BG)
.with_swipe_right_to_go_back(), .with_swipe_right_to_go_back(),
) )
@ -824,7 +819,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break()); paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break());
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value)); paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
} }
let mut page: ButtonPage<_, StrBuffer> = let mut page: ButtonPage<_> =
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?; ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
if cancel_arrow { if cancel_arrow {
page = page.with_cancel_arrow() page = page.with_cancel_arrow()
@ -832,7 +827,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
if info_button { if info_button {
page = page.with_swipe_left(); page = page.with_swipe_left();
} }
let mut frame = Frame::left_aligned(theme::label_title(), title, page); let mut frame = Frame::left_aligned(theme::label_title(), title.into(), page);
if info_button { if info_button {
frame = frame.with_info_button(); frame = frame.with_info_button();
} }
@ -864,8 +859,8 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
let tr_title: StrBuffer = TR::modify_amount__title.try_into()?; let tr_title: StrBuffer = TR::modify_amount__title.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
tr_title, tr_title.into(),
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG) ButtonPage::<_>::new(paragraphs, theme::BG)
.with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.try_into()?)), .with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.try_into()?)),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -908,8 +903,8 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
let obj = LayoutObj::new( let obj = LayoutObj::new(
Frame::left_aligned( Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG) ButtonPage::<_>::new(paragraphs, theme::BG)
.with_hold()? .with_hold()?
.with_swipe_left(), .with_swipe_left(),
) )
@ -963,7 +958,7 @@ fn new_show_modal(
title, title,
Button::cancel_confirm( Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL), Button::with_icon(theme::ICON_CANCEL),
Button::with_text(button).styled(button_style), Button::with_text(button.into()).styled(button_style),
false, false,
), ),
) )
@ -977,9 +972,9 @@ fn new_show_modal(
IconDialog::new( IconDialog::new(
icon, icon,
title, title,
theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| { theme::button_bar(Button::with_text(button.into()).styled(button_style).map(
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) |msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed),
})), )),
) )
.with_value(value) .with_value(value)
.with_description(description), .with_description(description),
@ -1008,7 +1003,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
let block = move |_args: &[Obj], kwargs: &Map| { let block = move |_args: &[Obj], kwargs: &Map| {
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let app_name: StrBuffer = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?; let app_name: StrBuffer = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
let icon: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?; let icon: Option<TString> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?; let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
// Cache the page count so that we can move `accounts` into the closure. // Cache the page count so that we can move `accounts` into the closure.
@ -1023,14 +1018,17 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
let controls = Button::cancel_confirm( let controls = Button::cancel_confirm(
Button::with_icon(theme::ICON_CANCEL), Button::with_icon(theme::ICON_CANCEL),
Button::<StrBuffer>::with_text(TR::buttons__confirm.try_into()?) Button::with_text(TR::buttons__confirm.try_into()?).styled(theme::button_confirm()),
.styled(theme::button_confirm()),
true, true,
); );
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls); let fido_page = FidoConfirm::new(app_name.into(), get_page, page_count, icon, controls);
let obj = LayoutObj::new(Frame::centered(theme::label_title(), title, fido_page))?; let obj = LayoutObj::new(Frame::centered(
theme::label_title(),
title.into(),
fido_page,
))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1098,7 +1096,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
title, title,
Button::cancel_confirm( Button::cancel_confirm(
Button::with_icon(theme::ICON_BACK), Button::with_icon(theme::ICON_BACK),
Button::with_text(button).styled(theme::button_reset()), Button::with_text(button.into()).styled(theme::button_reset()),
true, true,
), ),
) )
@ -1128,10 +1126,10 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map)
let obj = if let Some(t) = title { let obj = if let Some(t) = title {
LayoutObj::new(Frame::left_aligned( LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
t, t.into(),
Dialog::new( Dialog::new(
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]), Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
theme::button_bar(Button::with_text(button).map(|msg| { theme::button_bar(Button::with_text(button.into()).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})), })),
), ),
@ -1142,7 +1140,7 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map)
theme::borders(), theme::borders(),
Dialog::new( Dialog::new(
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]), Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
theme::button_bar(Button::with_text(button).map(|msg| { theme::button_bar(Button::with_text(button.into()).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})), })),
), ),
@ -1185,11 +1183,11 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
} }
} }
let buttons = Button::cancel_info_confirm(button, info_button); let buttons = Button::cancel_info_confirm(button.into(), info_button.into());
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new(paragraphs.into_paragraphs(), buttons), Dialog::new(paragraphs.into_paragraphs(), buttons),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1214,9 +1212,9 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
.with_cancel_confirm(None, Some(button)) .with_cancel_confirm(None, Some(button.into()))
.with_confirm_style(theme::button_default()) .with_confirm_style(theme::button_default())
.with_back_button(), .with_back_button(),
))?; ))?;
@ -1240,8 +1238,8 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
let tr_title: StrBuffer = TR::coinjoin__title.try_into()?; let tr_title: StrBuffer = TR::coinjoin__title.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
tr_title, tr_title.into(),
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold()?, ButtonPage::<_>::new(paragraphs, theme::BG).with_hold()?,
))?; ))?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -1259,7 +1257,12 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
} else { } else {
None None
}; };
let obj = LayoutObj::new(PinKeyboard::new(prompt, subprompt, warning, allow_cancel))?; let obj = LayoutObj::new(PinKeyboard::new(
prompt.into(),
subprompt.into(),
warning,
allow_cancel,
))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1282,7 +1285,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma
let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?; let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?;
let obj = LayoutObj::new(MnemonicKeyboard::new( let obj = LayoutObj::new(MnemonicKeyboard::new(
Bip39Input::prefilled_word(prefill_word.as_ref()), Bip39Input::prefilled_word(prefill_word.as_ref()),
prompt, prompt.into(),
can_go_back, can_go_back,
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1297,7 +1300,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M
let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?; let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?;
let obj = LayoutObj::new(MnemonicKeyboard::new( let obj = LayoutObj::new(MnemonicKeyboard::new(
Slip39Input::prefilled_word(prefill_word.as_ref()), Slip39Input::prefilled_word(prefill_word.as_ref()),
prompt, prompt.into(),
can_go_back, can_go_back,
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1310,12 +1313,12 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?; let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let words: [StrBuffer; 3] = util::iter_into_array(words_iterable)?; let words: [TString<'static>; 3] = util::iter_into_array(words_iterable)?;
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]); let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]);
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new(paragraphs, Button::select_word(words)), Dialog::new(paragraphs, Button::select_word(words)),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1336,8 +1339,8 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG) ButtonPage::<_>::new(paragraphs.into_paragraphs(), theme::BG)
.with_hold()? .with_hold()?
.without_cancel(), .without_cancel(),
))?; ))?;
@ -1366,7 +1369,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
NumberInputDialog::new(min_count, max_count, count, callback)?, NumberInputDialog::new(min_count, max_count, count, callback)?,
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1394,7 +1397,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new( Dialog::new(
Checklist::from_paragraphs( Checklist::from_paragraphs(
theme::ICON_LIST_CURRENT, theme::ICON_LIST_CURRENT,
@ -1407,7 +1410,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
.with_check_width(theme::CHECKLIST_CHECK_WIDTH) .with_check_width(theme::CHECKLIST_CHECK_WIDTH)
.with_current_offset(theme::CHECKLIST_CURRENT_OFFSET) .with_current_offset(theme::CHECKLIST_CURRENT_OFFSET)
.with_done_offset(theme::CHECKLIST_DONE_OFFSET), .with_done_offset(theme::CHECKLIST_DONE_OFFSET),
theme::button_bar(Button::with_text(button).map(|msg| { theme::button_bar(Button::with_text(button.into()).map(|msg| {
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
})), })),
), ),
@ -1440,10 +1443,10 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
let obj = if info_button { let obj = if info_button {
LayoutObj::new(Frame::left_aligned( LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
notification, notification.into(),
Dialog::new( Dialog::new(
paragraphs, paragraphs,
Button::<StrBuffer>::cancel_info_confirm( Button::cancel_info_confirm(
TR::buttons__continue.try_into()?, TR::buttons__continue.try_into()?,
TR::buttons__more_info.try_into()?, TR::buttons__more_info.try_into()?,
), ),
@ -1452,8 +1455,11 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
} else { } else {
LayoutObj::new(Frame::left_aligned( LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
notification, notification.into(),
Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))), Dialog::new(
paragraphs,
Button::cancel_confirm_text(None, Some(button.into())),
),
))? ))?
}; };
Ok(obj.into()) Ok(obj.into())
@ -1477,7 +1483,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
title, title.into(),
Dialog::new(paragraphs, SelectWordCount::new()), Dialog::new(paragraphs, SelectWordCount::new()),
))?; ))?;
Ok(obj.into()) Ok(obj.into())
@ -1494,14 +1500,13 @@ extern "C" fn new_show_group_share_success(
let lines_iterable: Obj = kwargs.get(Qstr::MP_QSTR_lines)?; let lines_iterable: Obj = kwargs.get(Qstr::MP_QSTR_lines)?;
let lines: [StrBuffer; 4] = util::iter_into_array(lines_iterable)?; let lines: [StrBuffer; 4] = util::iter_into_array(lines_iterable)?;
let obj = LayoutObj::new(IconDialog::new_shares( let obj =
lines, LayoutObj::new(IconDialog::new_shares(
theme::button_bar( lines,
Button::<StrBuffer>::with_text(TR::buttons__continue.try_into()?).map(|msg| { theme::button_bar(Button::with_text(TR::buttons__continue.try_into()?).map(
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) |msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed),
}), )),
), ))?;
))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1522,8 +1527,8 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
let tr_title: StrBuffer = TR::recovery__title_remaining_shares.try_into()?; let tr_title: StrBuffer = TR::recovery__title_remaining_shares.try_into()?;
let obj = LayoutObj::new(Frame::left_aligned( let obj = LayoutObj::new(Frame::left_aligned(
theme::label_title(), theme::label_title(),
tr_title, tr_title.into(),
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG) ButtonPage::<_>::new(paragraphs.into_paragraphs(), theme::BG)
.with_cancel_confirm(None, Some(TR::buttons__continue.try_into()?)) .with_cancel_confirm(None, Some(TR::buttons__continue.try_into()?))
.with_confirm_style(theme::button_default()) .with_confirm_style(theme::button_default())
.without_cancel(), .without_cancel(),
@ -1541,7 +1546,7 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
// Description updates are received as &str and we need to provide a way to // Description updates are received as &str and we need to provide a way to
// convert them to StrBuffer. // convert them to StrBuffer.
let obj = LayoutObj::new(Progress::new(title, indeterminate, description))?; let obj = LayoutObj::new(Progress::new(title.into(), indeterminate, description))?;
Ok(obj.into()) Ok(obj.into())
}; };
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1556,7 +1561,7 @@ extern "C" fn new_show_progress_coinjoin(n_args: usize, args: *const Obj, kwargs
// The second type parameter is actually not used in `new()` but we need to // The second type parameter is actually not used in `new()` but we need to
// provide it. // provide it.
let progress = CoinJoinProgress::<_, Never>::new(title, indeterminate)?; let progress = CoinJoinProgress::<Never>::new(title.into(), indeterminate)?;
let obj = if time_ms > 0 && indeterminate { let obj = if time_ms > 0 && indeterminate {
let timeout = Timeout::new(time_ms); let timeout = Timeout::new(time_ms);
LayoutObj::new((timeout, progress.map(|_msg| None)))? LayoutObj::new((timeout, progress.map(|_msg| None)))?
@ -1635,7 +1640,7 @@ extern "C" fn new_confirm_firmware_update(
let title_str = TR::firmware_update__title.try_into()?; let title_str = TR::firmware_update__title.try_into()?;
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered(); let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered();
let msg = Label::left_aligned(description, theme::TEXT_NORMAL); let msg = Label::left_aligned(description.into(), theme::TEXT_NORMAL);
let left = let left =
Button::with_text(TR::buttons__cancel.try_into()?).styled(theme::button_default()); Button::with_text(TR::buttons__cancel.try_into()?).styled(theme::button_default());
@ -1645,7 +1650,7 @@ extern "C" fn new_confirm_firmware_update(
let obj = LayoutObj::new( let obj = LayoutObj::new(
Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info( Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info(
TR::firmware_update__title_fingerprint.try_into()?, TR::firmware_update__title_fingerprint.try_into()?,
fingerprint, fingerprint.into(),
theme::button_moreinfo(), theme::button_moreinfo(),
), ),
)?; )?;

@ -11,9 +11,11 @@ use crate::ui::{
}; };
#[cfg(not(feature = "micropython"))] #[cfg(not(feature = "micropython"))]
use crate::strutil::TString;
// SAFETY: Actually safe but see below // SAFETY: Actually safe but see below
unsafe fn get_str(text: &str) -> &str { #[cfg(not(feature = "micropython"))]
text unsafe fn get_str(text: &str) -> TString {
TString::Str(text)
} }
#[cfg(feature = "micropython")] #[cfg(feature = "micropython")]
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not // SAFETY: The caller is responsible for ensuring that the StrBuffer does not
@ -29,7 +31,7 @@ pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) {
let msg = unsafe { get_str(msg) }; let msg = unsafe { get_str(msg) };
let footer = unsafe { get_str(footer) }; let footer = unsafe { get_str(footer) };
let mut frame = ErrorScreen::new(title, msg, footer); let mut frame = ErrorScreen::new(title.into(), msg.into(), footer.into());
frame.place(constant::screen()); frame.place(constant::screen());
frame.paint(); frame.paint();
} }

Loading…
Cancel
Save