diff --git a/core/embed/rust/src/ui/component/label.rs b/core/embed/rust/src/ui/component/label.rs index 3c7b839dbe..6399210010 100644 --- a/core/embed/rust/src/ui/component/label.rs +++ b/core/embed/rust/src/ui/component/label.rs @@ -1,22 +1,22 @@ -use crate::ui::{ - component::{Component, Event, EventCtx, Never}, - display::Font, - geometry::{Alignment, Insets, Offset, Point, Rect}, +use crate::{ + strutil::TString, + ui::{ + component::{Component, Event, EventCtx, Never}, + display::Font, + geometry::{Alignment, Insets, Offset, Point, Rect}, + }, }; use super::{text::TextStyle, TextLayout}; -pub struct Label { - text: T, +pub struct Label<'a> { + text: TString<'a>, layout: TextLayout, vertical: Alignment, } -impl Label -where - T: AsRef, -{ - pub fn new(text: T, align: Alignment, style: TextStyle) -> Self { +impl<'a> Label<'a> { + pub fn new(text: TString<'a>, align: Alignment, style: TextStyle) -> Self { Self { text, 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) } - 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) } - pub fn centered(text: T, style: TextStyle) -> Self { + pub fn centered(text: TString<'a>, style: TextStyle) -> Self { Self::new(text, Alignment::Center, style) } @@ -41,11 +41,11 @@ where self } - pub fn text(&self) -> &T { + pub fn text(&self) -> &TString<'a> { &self.text } - pub fn set_text(&mut self, text: T) { + pub fn set_text(&mut self, text: TString<'a>) { self.text = text; } @@ -63,22 +63,22 @@ where pub fn max_size(&self) -> Offset { let font = self.font(); - Offset::new(font.text_width(self.text.as_ref()), font.text_max_height()) + let width = self.text.map(|c| font.text_width(c)); + Offset::new(width, font.text_max_height()) } pub fn text_height(&self, width: i16) -> i16 { let bounds = Rect::from_top_left_and_size(Point::zero(), Offset::new(width, i16::MAX)); - self.layout - .with_bounds(bounds) - .fit_text(self.text.as_ref()) - .height() + + self.text + .map(|c| self.layout.with_bounds(bounds).fit_text(c).height()) } pub fn text_area(&self) -> Rect { // XXX only works on single-line labels assert!(self.layout.bounds.height() <= self.font().text_max_height()); let available_width = self.layout.bounds.width(); - let width = self.font().text_width(self.text.as_ref()); + let width = self.text.map(|c| self.font().text_width(c)); let height = self.font().text_height(); let cursor = self.layout.initial_cursor(); let baseline = match self.alignment() { @@ -90,18 +90,13 @@ where } } -impl Component for Label -where - T: AsRef, -{ +impl Component for Label<'_> { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { let height = self - .layout - .with_bounds(bounds) - .fit_text(self.text.as_ref()) - .height(); + .text + .map(|c| self.layout.with_bounds(bounds).fit_text(c).height()); let diff = bounds.height() - height; let insets = match self.vertical { Alignment::Start => Insets::bottom(diff), @@ -117,7 +112,7 @@ where } fn paint(&mut self) { - self.layout.render_text(self.text.as_ref()); + self.text.map(|c| self.layout.render_text(c)); } #[cfg(feature = "ui_bounds")] @@ -127,12 +122,9 @@ where } #[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Label -where - T: AsRef, -{ +impl crate::trace::Trace for Label<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Label"); - t.string("text", self.text.as_ref().into()); + t.string("text", self.text); } } diff --git a/core/embed/rust/src/ui/component/marquee.rs b/core/embed/rust/src/ui/component/marquee.rs index fe38a8f222..c2f39a0819 100644 --- a/core/embed/rust/src/ui/component/marquee.rs +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -1,4 +1,5 @@ use crate::{ + strutil::TString, time::{Duration, Instant}, ui::{ animation::Animation, @@ -21,13 +22,13 @@ enum State { PauseRight, } -pub struct Marquee { +pub struct Marquee { area: Rect, pause_token: Option, min_offset: i16, max_offset: i16, state: State, - text: T, + text: TString<'static>, font: Font, fg: Color, bg: Color, @@ -35,11 +36,8 @@ pub struct Marquee { pause: Duration, } -impl Marquee -where - T: AsRef, -{ - pub fn new(text: T, font: Font, fg: Color, bg: Color) -> Self { +impl Marquee { + pub fn new(text: TString<'static>, font: Font, fg: Color, bg: Color) -> Self { Self { area: Rect::zero(), 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; } @@ -66,7 +64,7 @@ where } if let State::Initial = self.state { - let text_width = self.font.text_width(self.text.as_ref()); + let text_width = self.text.map(|t| self.font.text_width(t)); let max_offset = self.area.width() - text_width; self.min_offset = 0; @@ -122,21 +120,12 @@ where } pub fn paint_anim(&mut self, offset: i16) { - display::marquee( - self.area, - self.text.as_ref(), - offset, - self.font, - self.fg, - self.bg, - ); + self.text + .map(|t| display::marquee(self.area, t, offset, self.font, self.fg, self.bg)); } } -impl Component for Marquee -where - T: AsRef, -{ +impl Component for Marquee { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { @@ -228,12 +217,9 @@ where } #[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Marquee -where - T: AsRef, -{ +impl crate::trace::Trace for Marquee { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Marquee"); - t.string("text", self.text.as_ref().into()); + t.string("text", self.text); } } diff --git a/core/embed/rust/src/ui/model_tr/bootloader/intro.rs b/core/embed/rust/src/ui/model_tr/bootloader/intro.rs index df114fc4c8..3cb0e46376 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/intro.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/intro.rs @@ -1,7 +1,10 @@ -use crate::ui::{ - component::{Child, Component, Event, EventCtx, Label, Pad}, - geometry::{Alignment, Alignment2D, Rect}, - layout::simplified::ReturnToC, +use crate::{ + strutil::TString, + ui::{ + component::{Child, Component, Event, EventCtx, Label, Pad}, + geometry::{Alignment, Alignment2D, Rect}, + layout::simplified::ReturnToC, + }, }; use super::super::{ @@ -29,14 +32,14 @@ impl ReturnToC for IntroMsg { pub struct Intro<'a> { bg: Pad, - title: Child>, + title: Child>, buttons: Child, - text: Child>, - warn: Option>>, + text: Child>, + warn: Option>>, } 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 { bg: Pad::with_background(BLD_BG).with_clear(), 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()), 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(), )), } diff --git a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs index 3941e067e9..a807449f44 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs @@ -126,9 +126,10 @@ impl UIFeaturesBootloader for ModelTRFeatures { 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); @@ -136,10 +137,10 @@ impl UIFeaturesBootloader for ModelTRFeatures { } 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 = - Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); + let content = 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); show(&mut frame, false); @@ -169,44 +170,68 @@ impl UIFeaturesBootloader for ModelTRFeatures { "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( - fingerprint, + fingerprint.into(), TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen), ) .vertically_centered(); - let alert = - (!should_keep_seed).then_some(Label::left_aligned("Seed will be erased!", TEXT_NORMAL)); + let alert = (!should_keep_seed).then_some(Label::left_aligned( + "Seed will be erased!".into(), + TEXT_NORMAL, + )); - let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false) - .with_info_screen("FW FINGERPRINT", fingerprint); + let mut frame = Confirm::new( + BLD_BG, + title_str.into(), + message, + alert, + "INSTALL".into(), + false, + ) + .with_info_screen("FW FINGERPRINT".into(), fingerprint); run(&mut frame) } fn screen_wipe_confirm() -> u32 { - let message = Label::left_aligned("Seed and firmware will be erased!", TEXT_NORMAL) + let message = Label::left_aligned("Seed and firmware will be erased!".into(), TEXT_NORMAL) .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) } fn screen_unlock_bootloader_confirm() -> u32 { - let message = - Label::left_aligned("This action cannot be undone!", TEXT_NORMAL).vertically_centered(); + let message = Label::left_aligned("This action cannot be undone!".into(), TEXT_NORMAL) + .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) } 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 = - Label::centered("Please reconnect the\ndevice", TEXT_NORMAL).vertically_centered(); + let content = 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); show(&mut frame, false); @@ -227,7 +252,11 @@ impl UIFeaturesBootloader for ModelTRFeatures { unwrap!(version_str.push_str("\nby ")); 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) } @@ -268,20 +297,20 @@ impl UIFeaturesBootloader for ModelTRFeatures { } 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 = - Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); + let content = 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); show(&mut frame, false); } 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 = - Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered(); + let content = 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); show(&mut frame, false); diff --git a/core/embed/rust/src/ui/model_tr/component/address_details.rs b/core/embed/rust/src/ui/model_tr/component/address_details.rs index 45b73d80da..cbaaba8a27 100644 --- a/core/embed/rust/src/ui/model_tr/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tr/component/address_details.rs @@ -23,7 +23,7 @@ const QR_BORDER: i16 = 3; pub struct AddressDetails { qr_code: Qr, details_view: Paragraphs>, - xpub_view: Frame>, StrBuffer>, + xpub_view: Frame>>, xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>, current_page: usize, current_subpage: usize, @@ -149,7 +149,7 @@ impl AddressDetails { fn fill_xpub_page(&mut self, ctx: &mut EventCtx) { 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| { p.inner_mut().update(self.xpubs[i].1); p.change_page(0) diff --git a/core/embed/rust/src/ui/model_tr/component/bl_confirm.rs b/core/embed/rust/src/ui/model_tr/component/bl_confirm.rs index 34a1d52712..85e589b73a 100644 --- a/core/embed/rust/src/ui/model_tr/component/bl_confirm.rs +++ b/core/embed/rust/src/ui/model_tr/component/bl_confirm.rs @@ -20,14 +20,14 @@ pub enum ConfirmMsg { Confirm = 2, } -pub struct Confirm { +pub struct Confirm<'a> { bg: Pad, bg_color: Color, - title: TString<'static>, - message: Child>, - alert: Option>, - info_title: Option>, - info_text: Option>, + title: TString<'a>, + message: Child>, + alert: Option>, + info_title: Option>, + info_text: Option>, button_text: TString<'static>, buttons: ButtonController, /// Whether we are on the info screen (optional extra screen) @@ -35,25 +35,21 @@ pub struct Confirm { two_btn_confirm: bool, } -impl Confirm -where - U: AsRef, -{ - pub fn new>>( +impl<'a> Confirm<'a> { + pub fn new( bg_color: Color, - title: T, - message: Label, - alert: Option>, - button_text: T, + title: TString<'a>, + message: Label<'a>, + alert: Option>, + button_text: TString<'static>, two_btn_confirm: bool, ) -> Self { - let button_text = button_text.into(); let btn_layout = Self::get_button_layout_general(false, button_text, false, two_btn_confirm); Self { bg: Pad::with_background(bg_color).with_clear(), bg_color, - title: title.into(), + title, message: Child::new(message), alert, info_title: None, @@ -66,12 +62,8 @@ where } /// Adding optional info screen - pub fn with_info_screen>>( - mut self, - info_title: T, - info_text: Label, - ) -> Self { - self.info_title = Some(info_title.into()); + pub fn with_info_screen(mut self, info_title: TString<'a>, info_text: Label<'a>) -> Self { + self.info_title = Some(info_title); self.info_text = Some(info_text); self.buttons = ButtonController::new(self.get_button_layout()); self @@ -125,10 +117,7 @@ where } } -impl Component for Confirm -where - U: AsRef, -{ +impl Component for Confirm<'_> { type Msg = ConfirmMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -207,7 +196,7 @@ where fn paint(&mut self) { self.bg.paint(); - let display_top_left = |text: TString<'static>| { + let display_top_left = |text: TString| { text.map(|t| { display::text_top_left(Point::zero(), t, Font::BOLD, WHITE, self.bg_color) }); @@ -234,10 +223,7 @@ where } #[cfg(feature = "ui_debug")] -impl crate::trace::Trace for Confirm -where - U: AsRef, -{ +impl crate::trace::Trace for Confirm<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("BlConfirm"); } diff --git a/core/embed/rust/src/ui/model_tr/component/error.rs b/core/embed/rust/src/ui/model_tr/component/error.rs index 29624f40bd..52818a342b 100644 --- a/core/embed/rust/src/ui/model_tr/component/error.rs +++ b/core/embed/rust/src/ui/model_tr/component/error.rs @@ -1,8 +1,11 @@ -use crate::ui::{ - component::{Child, Component, Event, EventCtx, Label, Never, Pad}, - constant::{screen, WIDTH}, - display, - geometry::{Alignment2D, Offset, Point, Rect}, +use crate::{ + strutil::TString, + ui::{ + component::{Child, Component, Event, EventCtx, Label, Never, Pad}, + constant::{screen, WIDTH}, + display, + geometry::{Alignment2D, Offset, Point, Rect}, + }, }; use super::super::{ @@ -13,17 +16,17 @@ use super::super::{ const FOOTER_AREA_HEIGHT: i16 = 20; const DIVIDER_POSITION: i16 = 43; -pub struct ErrorScreen { +pub struct ErrorScreen<'a> { bg: Pad, show_icons: bool, - title: Child>, - message: Child>, - footer: Child>, + title: Child>, + message: Child>, + footer: Child>, area: Rect, } -impl> ErrorScreen { - pub fn new(title: T, message: T, footer: T) -> Self { +impl<'a> ErrorScreen<'a> { + pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self { let title = Label::centered(title, theme::TEXT_BOLD); let message = Label::centered(message, theme::TEXT_NORMAL).vertically_centered(); let footer = Label::centered(footer, theme::TEXT_NORMAL).vertically_centered(); @@ -39,7 +42,7 @@ impl> ErrorScreen { } } -impl> Component for ErrorScreen { +impl Component for ErrorScreen<'_> { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { diff --git a/core/embed/rust/src/ui/model_tr/component/flow.rs b/core/embed/rust/src/ui/model_tr/component/flow.rs index b161566d77..85a1bc8c41 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::StringType, + strutil::TString, ui::{ component::{Child, Component, ComponentExt, Event, EventCtx, Pad, Paginate}, geometry::Rect, @@ -11,17 +11,16 @@ use super::{ ButtonControllerMsg, ButtonLayout, ButtonPos, CancelInfoConfirmMsg, FlowPages, Page, ScrollBar, }; -pub struct Flow +pub struct Flow where - F: Fn(usize) -> Page, - T: StringType + Clone, + F: Fn(usize) -> Page, { /// Function to get pages from - pages: FlowPages, + pages: FlowPages, /// Instance of the current Page - current_page: Page, + current_page: Page, /// Title being shown at the top in bold - title: Option>, + title: Option, scrollbar: Child<ScrollBar>, content_area: Rect, title_area: Rect, @@ -35,12 +34,11 @@ where ignore_second_button_ms: Option<u32>, } -impl<F, T> Flow<F, T> +impl<F> Flow<F> where - F: Fn(usize) -> Page<T>, - T: StringType + Clone, + F: Fn(usize) -> Page, { - pub fn new(pages: FlowPages<F, T>) -> Self { + pub fn new(pages: FlowPages<F>) -> Self { let current_page = pages.get(0); let title = current_page.title().map(Title::new); Self { @@ -64,7 +62,7 @@ where /// Adding a common title to all pages. The title will not be colliding /// 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 } @@ -196,10 +194,9 @@ where } } -impl<F, T> Component for Flow<F, T> +impl<F> Component for Flow<F> where - F: Fn(usize) -> Page<T>, - T: StringType + Clone, + F: Fn(usize) -> Page, { type Msg = CancelInfoConfirmMsg; @@ -313,10 +310,9 @@ where // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<F, T> crate::trace::Trace for Flow<F, T> +impl<F> crate::trace::Trace for Flow<F> where - F: Fn(usize) -> Page<T>, - T: StringType + Clone, + F: Fn(usize) -> Page, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Flow"); diff --git a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs index 4ccfef854b..5ffd92c033 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow_pages.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow_pages.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::StringType, + strutil::TString, ui::{ component::{base::Component, FormattedText, Paginate}, geometry::Rect, @@ -21,10 +21,9 @@ const MAX_OPS_PER_PAGE: usize = 15; /// have theoretically unlimited number of pages without triggering SO. /// (Currently only the current page is stored on stack - in /// `Flow::current_page`.) -pub struct FlowPages<F, T> +pub struct FlowPages<F> where - T: StringType + Clone, - F: Fn(usize) -> Page<T>, + F: Fn(usize) -> Page, { /// Function/closure that will return appropriate page on demand. get_page: F, @@ -32,10 +31,9 @@ where page_count: usize, } -impl<F, T> FlowPages<F, T> +impl<F> FlowPages<F> where - F: Fn(usize) -> Page<T>, - T: StringType + Clone, + F: Fn(usize) -> Page, { pub fn new(get_page: F, page_count: usize) -> Self { Self { @@ -45,7 +43,7 @@ where } /// 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) } @@ -74,24 +72,18 @@ where } #[derive(Clone)] -pub struct Page<T> -where - T: StringType + Clone, -{ +pub struct Page { formatted: FormattedText, btn_layout: ButtonLayout, btn_actions: ButtonActions, current_page: usize, page_count: usize, - title: Option<T>, + title: Option<TString<'static>>, slim_arrows: bool, } // For `layout.rs` -impl<T> Page<T> -where - T: StringType + Clone, -{ +impl Page { pub fn new( btn_layout: ButtonLayout, btn_actions: ButtonActions, @@ -112,12 +104,9 @@ where } // For `flow.rs` -impl<T> Page<T> -where - T: StringType + Clone, -{ +impl Page { /// 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 } @@ -180,8 +169,8 @@ where self.btn_actions } - pub fn title(&self) -> Option<T> { - self.title.clone() + pub fn title(&self) -> Option<TString<'static>> { + self.title } pub fn has_prev_page(&self) -> bool { @@ -208,10 +197,7 @@ where } // Pagination -impl<T> Paginate for Page<T> -where - T: StringType + Clone, -{ +impl Paginate for Page { fn page_count(&mut self) -> usize { self.formatted.page_count() } @@ -227,10 +213,7 @@ where use crate::ui::component::text::layout::LayoutFit; #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Page<T> -where - T: StringType + Clone, -{ +impl crate::trace::Trace for Page { fn trace(&self, t: &mut dyn crate::trace::Tracer) { use crate::ui::component::text::layout::trace::TraceSink; use core::cell::Cell; @@ -238,7 +221,7 @@ where t.component("Page"); if let Some(title) = &self.title { // 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("page_count", self.page_count as i64); diff --git a/core/embed/rust/src/ui/model_tr/component/frame.rs b/core/embed/rust/src/ui/model_tr/component/frame.rs index 0cdce56ecd..b9d8d3f7b1 100644 --- a/core/embed/rust/src/ui/model_tr/component/frame.rs +++ b/core/embed/rust/src/ui/model_tr/component/frame.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::StringType, + strutil::TString, ui::{ component::{Child, Component, ComponentExt, Event, EventCtx, Paginate}, geometry::{Insets, Rect}, @@ -9,21 +9,19 @@ use crate::{ use super::{super::constant, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar}; /// Component for holding another component and displaying a title. -pub struct Frame<T, U> +pub struct Frame<T> where T: Component, - U: StringType, { - title: Title<U>, + title: Title, content: Child<T>, } -impl<T, U> Frame<T, U> +impl<T> Frame<T> where T: Component, - U: StringType + Clone, { - pub fn new(title: U, content: T) -> Self { + pub fn new(title: TString<'static>, content: T) -> Self { Self { title: Title::new(title), content: Child::new(content), @@ -40,7 +38,7 @@ where 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); } @@ -56,10 +54,9 @@ where } } -impl<T, U> Component for Frame<T, U> +impl<T> Component for Frame<T> where T: Component, - U: StringType + Clone, { type Msg = T::Msg; @@ -85,10 +82,9 @@ where } } -impl<T, U> Paginate for Frame<T, U> +impl<T> Paginate for Frame<T> where T: Component + Paginate, - U: StringType + Clone, { fn page_count(&mut self) -> usize { self.content.page_count() @@ -106,20 +102,18 @@ pub trait ScrollableContent { /// Component for holding another component and displaying a title. /// Also is allocating space for a scrollbar. -pub struct ScrollableFrame<T, U> +pub struct ScrollableFrame<T> where T: Component + ScrollableContent, - U: StringType + Clone, { - title: Option<Child<Title<U>>>, + title: Option<Child<Title>>, scrollbar: ScrollBar, content: Child<T>, } -impl<T, U> ScrollableFrame<T, U> +impl<T> ScrollableFrame<T> where T: Component + ScrollableContent, - U: StringType + Clone, { pub fn new(content: T) -> Self { Self { @@ -133,16 +127,15 @@ where 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 } } -impl<T, U> Component for ScrollableFrame<T, U> +impl<T> Component for ScrollableFrame<T> where T: Component + ScrollableContent, - U: StringType + Clone, { type Msg = T::Msg; @@ -209,10 +202,9 @@ where // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for Frame<T, U> +impl<T> crate::trace::Trace for Frame<T> where T: crate::trace::Trace + Component, - U: StringType + Clone, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Frame"); @@ -222,10 +214,9 @@ where } #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for ScrollableFrame<T, U> +impl<T> crate::trace::Trace for ScrollableFrame<T> where T: crate::trace::Trace + Component + ScrollableContent, - U: StringType + Clone, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("ScrollableFrame"); diff --git a/core/embed/rust/src/ui/model_tr/component/homescreen.rs b/core/embed/rust/src/ui/model_tr/component/homescreen.rs index eb60413c7d..f3f2f6b439 100644 --- a/core/embed/rust/src/ui/model_tr/component/homescreen.rs +++ b/core/embed/rust/src/ui/model_tr/component/homescreen.rs @@ -1,7 +1,5 @@ use crate::{ - error::Error, - micropython::buffer::StrBuffer, - strutil::StringType, + strutil::TString, translations::TR, trezorhal::usb::usb_configured, ui::{ @@ -54,29 +52,27 @@ enum CurrentScreen { Loader, } -pub struct Homescreen<T> -where - T: StringType, -{ +pub struct Homescreen { // 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 - label: Label<T>, - notification: Option<(T, u8)>, + label: Label<'static>, + notification: Option<(TString<'static>, u8)>, /// Used for HTC functionality to lock device from homescreen invisible_buttons: Child<ButtonController>, /// Holds the loader component - loader: Option<Child<ProgressLoader<T>>>, + loader: Option<Child<ProgressLoader>>, /// Whether to show the loader or not show_loader: bool, /// Which screen is currently shown current_screen: CurrentScreen, } -impl<T> Homescreen<T> -where - T: StringType + Clone, -{ - pub fn new(label: T, notification: Option<(T, u8)>, loader_description: Option<T>) -> Self { +impl Homescreen { + pub fn new( + label: TString<'static>, + notification: Option<(TString<'static>, u8)>, + loader_description: Option<TString<'static>>, + ) -> Self { // Buttons will not be visible, we only need both left and right to be existing // so we can get the events from them. let invisible_btn_layout = ButtonLayout::text_none_text("".into(), "".into()); @@ -114,11 +110,11 @@ where .map_translated(|t| display_center(baseline, t, NOTIFICATION_FONT)); } else if let Some((notification, _level)) = &self.notification { self.fill_notification_background(); - display_center(baseline, notification.as_ref(), NOTIFICATION_FONT); + notification.map(|c| display_center(baseline, c, NOTIFICATION_FONT)); // Painting warning icons in top corners when the text is short enough not to // collide with them let icon_width = NOTIFICATION_ICON.toif.width(); - let text_width = NOTIFICATION_FONT.text_width(notification.as_ref()); + let text_width = notification.map(|c| NOTIFICATION_FONT.text_width(c)); if AREA.width() >= text_width + (icon_width + 1) * 2 { NOTIFICATION_ICON.draw( AREA.top_left(), @@ -169,10 +165,7 @@ where } } -impl<T> Component for Homescreen<T> -where - T: StringType + Clone, -{ +impl Component for Homescreen { type Msg = (); fn place(&mut self, bounds: Rect) -> Rect { @@ -238,12 +231,9 @@ where } } -pub struct Lockscreen<T> -where - T: StringType, -{ - label: Child<Label<T>>, - instruction: Child<Label<StrBuffer>>, +pub struct Lockscreen<'a> { + label: Child<Label<'a>>, + instruction: Child<Label<'static>>, /// Used for unlocking the device from lockscreen invisible_buttons: Child<ButtonController>, /// Display coinjoin icon? @@ -252,11 +242,8 @@ where screensaver: bool, } -impl<T> Lockscreen<T> -where - T: StringType + Clone, -{ - pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Result<Self, Error> { +impl<'a> Lockscreen<'a> { + pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self { // Buttons will not be visible, we only need all three of them to be present, // so that even middle-click triggers the event. let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into()); @@ -265,23 +252,17 @@ where } else { TR::homescreen__click_to_unlock }; - Ok(Lockscreen { + Self { label: Child::new(Label::centered(label, theme::TEXT_BIG)), - instruction: Child::new(Label::centered( - instruction_str.try_into()?, - theme::TEXT_NORMAL, - )), + instruction: Child::new(Label::centered(instruction_str.into(), theme::TEXT_NORMAL)), invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)), coinjoin_icon: coinjoin_authorized.then_some(theme::ICON_COINJOIN), screensaver: !bootscreen, - }) + } } } -impl<T> Component for Lockscreen<T> -where - T: StringType + Clone, -{ +impl Component for Lockscreen<'_> { type Msg = (); fn place(&mut self, bounds: Rect) -> Rect { @@ -322,20 +303,14 @@ where } } -pub struct ConfirmHomescreen<T, F> -where - T: StringType, -{ - title: Child<Label<T>>, +pub struct ConfirmHomescreen<F> { + title: Child<Label<'static>>, buffer_func: F, buttons: Child<ButtonController>, } -impl<T, F> ConfirmHomescreen<T, F> -where - T: StringType + Clone, -{ - pub fn new(title: T, buffer_func: F) -> Self { +impl<F> ConfirmHomescreen<F> { + pub fn new(title: TString<'static>, buffer_func: F) -> Self { let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into()); ConfirmHomescreen { 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 - T: StringType + Clone, F: Fn() -> &'a [u8], { type Msg = CancelConfirmMsg; @@ -398,10 +372,7 @@ pub fn check_homescreen_format(toif: &Toif) -> bool { // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Homescreen<T> -where - T: StringType, -{ +impl crate::trace::Trace for Homescreen { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Homescreen"); t.child("label", &self.label); @@ -409,10 +380,7 @@ where } #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Lockscreen<T> -where - T: StringType, -{ +impl crate::trace::Trace for Lockscreen<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Lockscreen"); t.child("label", &self.label); @@ -420,10 +388,7 @@ where } #[cfg(feature = "ui_debug")] -impl<T, F> crate::trace::Trace for ConfirmHomescreen<T, F> -where - T: StringType, -{ +impl<F> crate::trace::Trace for ConfirmHomescreen<F> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("ConfirmHomescreen"); t.child("title", &self.title); diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs index d0b4c6336d..404e8d8baf 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::{StringType, TString}, + strutil::TString, translations::TR, trezorhal::random, ui::{ @@ -129,12 +129,12 @@ impl ChoiceFactory for ChoiceFactoryPIN { } /// Component for entering a PIN. -pub struct PinEntry<T: StringType + Clone> { +pub struct PinEntry<'a> { choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>, header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>, pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>, - prompt: T, - subprompt: T, + prompt: TString<'a>, + subprompt: TString<'a>, /// Whether we already show the "real" prompt (not the warning). showing_real_prompt: bool, show_real_pin: bool, @@ -142,26 +142,23 @@ pub struct PinEntry<T: StringType + Clone> { textbox: TextBox<MAX_PIN_LENGTH>, } -impl<T> PinEntry<T> -where - T: StringType + Clone, -{ - pub fn new(prompt: T, subprompt: T) -> Self { +impl<'a> PinEntry<'a> { + pub fn new(prompt: TString<'a>, subprompt: TString<'a>) -> Self { // 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 // at the beginning. (WRONG PIN will be replaced by real prompt after // 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 { ( false, TR::pin__title_wrong_pin.map_translated(|t| unwrap!(String::try_from(t))), - unwrap!(String::try_from(subprompt.as_ref())), + subprompt.map(|s| unwrap!(String::try_from(s))), ) } else { ( true, - unwrap!(String::try_from(prompt.as_ref())), + prompt.map(|s| unwrap!(String::try_from(s))), unwrap!(String::try_from(EMPTY_PIN_STR)), ) }; @@ -201,10 +198,10 @@ where /// Many possibilities, according to the PIN state. fn update_pin_line(&mut self, ctx: &mut EventCtx) { 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 used_font = Font::NORMAL; - unwrap!(String::try_from(self.subprompt.as_ref())) + self.subprompt.map(|s| unwrap!(String::try_from(s))) } else if self.is_empty() { unwrap!(String::try_from(EMPTY_PIN_STR)) } else if self.show_real_pin { @@ -234,7 +231,7 @@ where /// Showing the real prompt instead of WRONG PIN fn show_prompt(&mut self, ctx: &mut EventCtx) { self.header_line.mutate(ctx, |ctx, header_line| { - header_line.update_text(unwrap!(String::try_from(self.prompt.as_ref()))); + header_line.update_text(self.prompt.map(|s| unwrap!(String::try_from(s)))); header_line.request_complete_repaint(ctx); }); } @@ -252,10 +249,7 @@ where } } -impl<T> Component for PinEntry<T> -where - T: StringType + Clone, -{ +impl Component for PinEntry<'_> { type Msg = CancelConfirmMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -334,13 +328,10 @@ where // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for PinEntry<T> -where - T: StringType + Clone, -{ +impl crate::trace::Trace for PinEntry<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("PinKeyboard"); - t.string("subprompt", self.subprompt.as_ref().into()); + t.string("subprompt", self.subprompt); t.string("pin", self.textbox.content().into()); t.child("choice_page", &self.choice_page); } diff --git a/core/embed/rust/src/ui/model_tr/component/loader.rs b/core/embed/rust/src/ui/model_tr/component/loader.rs index 8016576cb4..dc05d5b8df 100644 --- a/core/embed/rust/src/ui/model_tr/component/loader.rs +++ b/core/embed/rust/src/ui/model_tr/component/loader.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::{StringType, TString}, + strutil::TString, time::{Duration, Instant}, ui::{ animation::Animation, @@ -247,22 +247,16 @@ impl LoaderStyleSheet { } } -pub struct ProgressLoader<T> -where - T: StringType, -{ - loader: Child<Progress<T>>, +pub struct ProgressLoader { + loader: Child<Progress>, duration_ms: u32, start_time: Option<Instant>, } -impl<T> ProgressLoader<T> -where - T: StringType + Clone, -{ +impl ProgressLoader { 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 { loader: Child::new( Progress::new(false, loader_description).with_icon(theme::ICON_LOCK_SMALL), @@ -300,10 +294,7 @@ where } } -impl<T> Component for ProgressLoader<T> -where - T: StringType + Clone, -{ +impl Component for ProgressLoader { type Msg = LoaderMsg; fn place(&mut self, bounds: Rect) -> Rect { diff --git a/core/embed/rust/src/ui/model_tr/component/progress.rs b/core/embed/rust/src/ui/model_tr/component/progress.rs index d512dc75a3..4b9da68f51 100644 --- a/core/embed/rust/src/ui/model_tr/component/progress.rs +++ b/core/embed/rust/src/ui/model_tr/component/progress.rs @@ -1,7 +1,7 @@ use core::mem; use crate::{ - strutil::StringType, + strutil::TString, ui::{ component::{ paginated::Paginate, @@ -21,11 +21,8 @@ const BOTTOM_DESCRIPTION_MARGIN: i16 = 10; const LOADER_Y_OFFSET_TITLE: i16 = -10; const LOADER_Y_OFFSET_NO_TITLE: i16 = -20; -pub struct Progress<T> -where - T: StringType, -{ - title: Option<Child<Label<T>>>, +pub struct Progress { + title: Option<Child<Label<'static>>>, value: u16, loader_y_offset: i16, indeterminate: bool, @@ -34,13 +31,10 @@ where icon: Icon, } -impl<T> Progress<T> -where - T: StringType, -{ +impl Progress { const AREA: Rect = constant::screen(); - pub fn new(indeterminate: bool, description: T) -> Self { + pub fn new(indeterminate: bool, description: TString<'static>) -> Self { Self { title: None, 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 } @@ -79,10 +73,7 @@ where } } -impl<T> Component for Progress<T> -where - T: StringType, -{ +impl Component for Progress { type Msg = Never; 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 (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().is_empty() { let (title, rest) = Self::AREA.split_top(self_title.inner().max_size().y); (title, rest, LOADER_Y_OFFSET_TITLE) } else { @@ -171,10 +162,7 @@ where // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Progress<T> -where - T: StringType, -{ +impl crate::trace::Trace for Progress { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Progress"); } diff --git a/core/embed/rust/src/ui/model_tr/component/result.rs b/core/embed/rust/src/ui/model_tr/component/result.rs index 0fea93cf40..6da84f8432 100644 --- a/core/embed/rust/src/ui/model_tr/component/result.rs +++ b/core/embed/rust/src/ui/model_tr/component/result.rs @@ -17,8 +17,8 @@ pub struct ResultScreen<'a> { fg_color: Color, bg_color: Color, icon: Icon, - message_top: Child<Label<&'static str>>, - message_bottom: Child<Label<&'a str>>, + message_top: Child<Label<'static>>, + message_bottom: Child<Label<'a>>, } impl<'a> ResultScreen<'a> { @@ -26,8 +26,8 @@ impl<'a> ResultScreen<'a> { fg_color: Color, bg_color: Color, icon: Icon, - title: Label<&'static str>, - content: Label<&'a str>, + title: Label<'static>, + content: Label<'a>, complete_draw: bool, ) -> 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; fn place(&mut self, bounds: Rect) -> Rect { diff --git a/core/embed/rust/src/ui/model_tr/component/title.rs b/core/embed/rust/src/ui/model_tr/component/title.rs index 1f1c116c58..f75d488df9 100644 --- a/core/embed/rust/src/ui/model_tr/component/title.rs +++ b/core/embed/rust/src/ui/model_tr/component/title.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::StringType, + strutil::TString, time::Instant, ui::{ component::{Component, Event, EventCtx, Marquee, Never}, @@ -10,24 +10,18 @@ use crate::{ use super::super::theme; -pub struct Title<T> -where - T: StringType, -{ +pub struct Title { area: Rect, - title: T, - marquee: Marquee<T>, + title: TString<'static>, + marquee: Marquee, needs_marquee: bool, centered: bool, } -impl<T> Title<T> -where - T: StringType + Clone, -{ - pub fn new(title: T) -> Self { +impl Title { + pub fn new(title: TString<'static>) -> Self { Self { - title: title.clone(), + title, marquee: Marquee::new(title, theme::FONT_HEADER, theme::FG, theme::BG), needs_marquee: false, area: Rect::zero(), @@ -40,14 +34,14 @@ where self } - pub fn get_text(&self) -> &str { - self.title.as_ref() + pub fn get_text(&self) -> TString { + self.title } - pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: T) { - self.title = new_text.clone(); - self.marquee.set_text(new_text.clone()); - let text_width = theme::FONT_HEADER.text_width(new_text.as_ref()); + pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: TString<'static>) { + self.title = new_text; + self.marquee.set_text(new_text); + let text_width = new_text.map(|s| theme::FONT_HEADER.text_width(s)); self.needs_marquee = text_width > self.area.width(); // Resetting the marquee to the beginning and starting it when necessary. self.marquee.reset(); @@ -57,42 +51,31 @@ where } /// 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 title_baseline = area.top_left() + Offset::y(text_height - 1); - display::text_left( - title_baseline, - title.as_ref(), - theme::FONT_HEADER, - theme::FG, - theme::BG, - ); + title.map(|s| { + display::text_left(title_baseline, s, theme::FONT_HEADER, theme::FG, theme::BG) + }); } /// 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 title_baseline = area.top_center() + Offset::y(text_height - 1); - display::text_center( - title_baseline, - title.as_ref(), - theme::FONT_HEADER, - theme::FG, - theme::BG, - ); + title.map(|s| { + display::text_center(title_baseline, s, theme::FONT_HEADER, theme::FG, theme::BG) + }); } } -impl<T> Component for Title<T> -where - T: StringType + Clone, -{ +impl Component for Title { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { self.area = bounds; self.marquee.place(bounds); - let width = theme::FONT_HEADER.text_width(self.title.as_ref()); + let width = self.title.map(|s| theme::FONT_HEADER.text_width(s)); self.needs_marquee = width > self.area.width(); bounds } @@ -121,12 +104,9 @@ where // DEBUG-ONLY SECTION BELOW #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Title<T> -where - T: StringType + Clone, -{ +impl crate::trace::Trace for Title { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Title"); - t.string("text", self.title.as_ref().into()); + t.string("text", self.title); } } diff --git a/core/embed/rust/src/ui/model_tr/layout.rs b/core/embed/rust/src/ui/model_tr/layout.rs index d949f72943..f3e41de2bf 100644 --- a/core/embed/rust/src/ui/model_tr/layout.rs +++ b/core/embed/rust/src/ui/model_tr/layout.rs @@ -99,10 +99,9 @@ where } } -impl<F, T> ComponentMsgObj for Flow<F, T> +impl<F> ComponentMsgObj for Flow<F> where - F: Fn(usize) -> Page<T>, - T: StringType + Clone, + F: Fn(usize) -> Page, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { @@ -119,10 +118,7 @@ where } } -impl<T> ComponentMsgObj for PinEntry<T> -where - T: StringType + Clone, -{ +impl ComponentMsgObj for PinEntry<'_> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { 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 T: ComponentMsgObj, - U: StringType + Clone, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { self.inner().msg_try_into_obj(msg) } } -impl<T, U> ComponentMsgObj for ScrollableFrame<T, U> +impl<T> ComponentMsgObj for ScrollableFrame<T> where T: ComponentMsgObj + ScrollableContent, - U: StringType + Clone, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { self.inner().msg_try_into_obj(msg) } } -impl<T> ComponentMsgObj for Progress<T> -where - T: StringType, -{ +impl ComponentMsgObj for Progress { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { unreachable!() } } -impl<T> ComponentMsgObj for Homescreen<T> -where - T: StringType + Clone, -{ +impl ComponentMsgObj for Homescreen { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { Ok(CANCELLED.as_obj()) } } -impl<T> ComponentMsgObj for Lockscreen<T> -where - T: StringType + Clone, -{ +impl<'a> ComponentMsgObj for Lockscreen<'a> { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { Ok(CANCELLED.as_obj()) } } -impl<'a, T, F> ComponentMsgObj for ConfirmHomescreen<T, F> +impl<'a, F> ComponentMsgObj for ConfirmHomescreen<F> where - T: StringType + Clone, F: Fn() -> &'a [u8], { 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> -where - U: AsRef<str>, -{ +impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { 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); if !title.as_ref().is_empty() { - frame = frame.with_title(title); + frame = frame.with_title(title.into()); } 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. 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()) }; @@ -457,10 +438,6 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], _kwargs: &Map| { - // cached allocated translations that get_page can reuse - let tr_title_success: StrBuffer = TR::words__title_success.try_into()?; - let tr_title_backup_wallet: StrBuffer = TR::backup__title_backup_wallet.try_into()?; - let get_page = move |page_index| match page_index { 0 => { let btn_layout = ButtonLayout::text_none_arrow_wide(TR::buttons__skip.into()); @@ -470,7 +447,8 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M .newline() .text_normal(TR::backup__it_should_be_backed_up_now); 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::words__title_success.into()) } 1 => { let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into()); @@ -478,8 +456,8 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(TR::backup__recover_anytime); let formatted = FormattedText::new(ops).vertically_centered(); - Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) - .with_title(tr_title_backup_wallet) + Page::new(btn_layout, btn_actions, formatted) + .with_title(TR::backup__title_backup_wallet.into()) } _ => unreachable!(), }; @@ -624,7 +602,7 @@ extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs } ops = ops.text_mono(address); 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); @@ -646,7 +624,7 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs: let btn_actions = ButtonActions::cancel_none_confirm(); let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount); 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); @@ -686,7 +664,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma .text_mono(fee_amount); let formatted = FormattedText::new(ops); - Page::<StrBuffer>::new(btn_layout, btn_actions, formatted) + Page::new(btn_layout, btn_actions, formatted) } 1 => { // Fee rate info @@ -748,9 +726,6 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m let cancel_cross: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_cross, false)?; let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?; - // cached allocated translated strings that get_page can reuse - let tr_title_fee = TR::confirm_total__title_fee.try_into()?; - let get_page = move |page_index| { match page_index { 0 => { @@ -771,7 +746,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m .text_mono(fee_value); 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 => { // Other information @@ -794,7 +769,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m let formatted = FormattedText::new(ops).vertically_centered(); Page::new(btn_layout, btn_actions, formatted) - .with_title(tr_title_fee) + .with_title(TR::confirm_total__title_fee.into()) .with_slim_arrows() } _ => unreachable!(), @@ -829,7 +804,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut }; let ops = OpTextLayout::new(style).text_mono(address); 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); @@ -842,11 +817,11 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut /// General pattern of most tutorial screens. /// (title, text, btn_layout, btn_actions, text_y_offset) fn tutorial_screen( - title: StrBuffer, + title: TString<'static>, text: TR, btn_layout: ButtonLayout, btn_actions: ButtonActions, -) -> Page<StrBuffer> { +) -> Page { let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text); let formatted = FormattedText::new(ops).vertically_centered(); Page::new(btn_layout, btn_actions, formatted).with_title(title) @@ -856,15 +831,6 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj let block = |_args: &[Obj], _kwargs: &Map| { const PAGE_COUNT: usize = 7; - // cached allocated translated strings that get_page can reuse - let tr_title_hello: StrBuffer = TR::tutorial__title_hello.try_into()?; - let tr_hold_to_confirm: StrBuffer = TR::buttons__hold_to_confirm.try_into()?; - let tr_title_screen_scroll: StrBuffer = TR::tutorial__title_screen_scroll.try_into()?; - let tr_confirm: StrBuffer = TR::buttons__confirm.try_into()?; - let tr_title_tutorial_complete: StrBuffer = - TR::tutorial__title_tutorial_complete.try_into()?; - let tr_title_skip: StrBuffer = TR::tutorial__title_skip.try_into()?; - let get_page = move |page_index| { // Lazy-loaded list of screens to show, with custom content, // buttons and actions triggered by these buttons. @@ -874,7 +840,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj match page_index { // title, text, btn_layout, btn_actions 0 => tutorial_screen( - tr_title_hello, + TR::tutorial__title_hello.into(), TR::tutorial__welcome_press_right, ButtonLayout::cancel_none_arrow(), ButtonActions::last_none_next(), @@ -886,25 +852,25 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj ButtonActions::prev_none_next(), ), 2 => tutorial_screen( - tr_hold_to_confirm, + TR::buttons__hold_to_confirm.into(), TR::tutorial__press_and_hold, ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()), ButtonActions::prev_none_next(), ), 3 => tutorial_screen( - tr_title_screen_scroll, + TR::tutorial__title_screen_scroll.into(), TR::tutorial__scroll_down, ButtonLayout::arrow_none_text(TR::buttons__continue.into()), ButtonActions::prev_none_next(), ), 4 => tutorial_screen( - tr_confirm, + TR::buttons__confirm.into(), TR::tutorial__middle_click, ButtonLayout::none_armed_none(TR::buttons__confirm.into()), ButtonActions::none_next_none(), ), 5 => tutorial_screen( - tr_title_tutorial_complete, + TR::tutorial__title_tutorial_complete.into(), TR::tutorial__ready_to_use, ButtonLayout::text_none_text( TR::buttons__again.into(), @@ -913,7 +879,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj ButtonActions::beginning_none_confirm(), ), 6 => tutorial_screen( - tr_title_skip, + TR::tutorial__title_skip.into(), TR::tutorial__sure_you_want_skip, ButtonLayout::arrow_none_text(TR::buttons__skip.into()), ButtonActions::beginning_none_cancel(), @@ -1021,11 +987,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 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 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()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1080,14 +1046,14 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map .text_bold(account); 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); // Returning the page index in case of confirmation. let obj = LayoutObj::new( Flow::new(pages) - .with_common_title(title) + .with_common_title(title.into()) .with_return_confirmed_index(), )?; Ok(obj.into()) @@ -1115,7 +1081,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map ops = ops.text_normal(description); } 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 obj = LayoutObj::new(Flow::new(pages))?; @@ -1132,7 +1098,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 content = Frame::new( - title, + title.into(), Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]), ); let obj = if time_ms == 0 { @@ -1189,7 +1155,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma .newline() .text_bold(TR::addr_mismatch__support_url); 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); @@ -1222,7 +1188,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu } let obj = LayoutObj::new(Frame::new( - title, + title.into(), ShowMore::<Paragraphs<ParagraphVecShort>>::new( paragraphs.into_paragraphs(), verb_cancel, @@ -1291,7 +1257,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 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()) }; @@ -1302,7 +1268,9 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m let block = |_args: &[Obj], kwargs: &Map| { 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()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1310,13 +1278,13 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = |_args: &[Obj], kwargs: &Map| { - let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; + let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?; let prefill_word: StrBuffer = kwargs.get(Qstr::MP_QSTR_prefill_word)?.try_into()?; let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?; let obj = LayoutObj::new( Frame::new( - prompt, + prompt.into(), WordlistEntry::prefilled_word( prefill_word.as_ref(), WordlistType::Bip39, @@ -1338,7 +1306,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M let obj = LayoutObj::new( Frame::new( - prompt, + prompt.into(), WordlistEntry::prefilled_word( prefill_word.as_ref(), WordlistType::Slip39, @@ -1363,7 +1331,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 let obj = LayoutObj::new( Frame::new( - description, + description.into(), SimpleChoice::new(words, false) .with_show_incomplete() .with_return_index(), @@ -1402,7 +1370,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 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()) }; @@ -1497,7 +1466,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu .collect(); 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()) }; @@ -1542,9 +1511,9 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma .and_then(Obj::try_into_option) .unwrap_or(None); - let mut progress = Progress::new(indeterminate, description); + let mut progress = Progress::new(indeterminate, description.into()); if let Some(title) = title { - progress = progress.with_title(title); + progress = progress.with_title(title.into()); }; // Description updates are received as &str and we need to provide a way to @@ -1590,9 +1559,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 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 obj = LayoutObj::new(Homescreen::new(label, notification, loader_description))?; + let obj = LayoutObj::new(Homescreen::new( + label.into(), + notification, + loader_description, + ))?; if skip_first_paint { obj.skip_first_paint(); } @@ -1611,7 +1584,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 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 { obj.skip_first_paint(); } @@ -1631,16 +1608,27 @@ extern "C" fn new_confirm_firmware_update( let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?; 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( - fingerprint, + fingerprint.into(), theme::TEXT_NORMAL.with_line_breaking(LineBreaking::BreakWordsNoHyphen), ) .vertically_centered(); let obj = LayoutObj::new( - Confirm::new(theme::BG, title, message, None, TR::buttons__install, false) - .with_info_screen(TR::firmware_update__title_fingerprint, fingerprint), + Confirm::new( + theme::BG, + title.into(), + message, + None, + TR::buttons__install.as_tstring(), + false, + ) + .with_info_screen( + TR::firmware_update__title_fingerprint.as_tstring(), + fingerprint, + ), )?; Ok(obj.into()) }; diff --git a/core/embed/rust/src/ui/model_tr/screens.rs b/core/embed/rust/src/ui/model_tr/screens.rs index a9e45db7cb..89efbf00d4 100644 --- a/core/embed/rust/src/ui/model_tr/screens.rs +++ b/core/embed/rust/src/ui/model_tr/screens.rs @@ -1,31 +1,11 @@ -#[cfg(feature = "micropython")] -use crate::micropython::buffer::StrBuffer; use crate::ui::{ component::base::Component, constant::screen, display, model_tr::component::WelcomeScreen, }; use super::{component::ErrorScreen, constant}; -#[cfg(not(feature = "micropython"))] -// SAFETY: Actually safe but see below -unsafe fn get_str(text: &str) -> &str { - text -} -#[cfg(feature = "micropython")] -// SAFETY: The caller is responsible for ensuring that the StrBuffer does not -// escape the lifetime of the original &str. -unsafe fn get_str(text: &str) -> StrBuffer { - unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) } -} - pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) { - // SAFETY: these will get placed into `frame` which does not outlive this - // function - let title = unsafe { get_str(title) }; - let msg = unsafe { get_str(msg) }; - 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.paint(); } diff --git a/core/embed/rust/src/ui/model_tt/bootloader/intro.rs b/core/embed/rust/src/ui/model_tt/bootloader/intro.rs index b113c9c6a8..95b61ddaff 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/intro.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/intro.rs @@ -1,14 +1,17 @@ -use crate::ui::{ - component::{Child, Component, Event, EventCtx, Label, Pad}, - constant::screen, - display::Icon, - geometry::{Alignment, Insets, Point, Rect}, - model_tt::{ - component::{Button, ButtonMsg::Clicked}, - constant::WIDTH, - 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, +use crate::{ + strutil::TString, + ui::{ + component::{Child, Component, Event, EventCtx, Label, Pad}, + constant::screen, + display::Icon, + geometry::{Alignment, Insets, Point, Rect}, + model_tt::{ + component::{Button, ButtonMsg::Clicked}, + constant::WIDTH, + 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> { bg: Pad, - title: Child<Label<&'a str>>, - menu: Child<Button<&'static str>>, - host: Child<Button<&'static str>>, - text: Child<Label<&'a str>>, - warn: Option<Child<Label<&'a str>>>, + title: Child<Label<'a>>, + menu: Child<Button>, + host: Child<Button>, + text: Child<Label<'a>>, + warn: Option<Child<Label<'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 { bg: Pad::with_background(BLD_BG).with_clear(), 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()) .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()), 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(), )), } diff --git a/core/embed/rust/src/ui/model_tt/bootloader/menu.rs b/core/embed/rust/src/ui/model_tt/bootloader/menu.rs index 9cbf43df1b..7d0d664b49 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/menu.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/menu.rs @@ -29,10 +29,10 @@ pub enum MenuMsg { pub struct Menu { bg: Pad, - title: Child<Label<&'static str>>, - close: Child<Button<&'static str>>, - reboot: Child<Button<&'static str>>, - reset: Child<Button<&'static str>>, + title: Child<Label<'static>>, + close: Child<Button>, + reboot: Child<Button>, + reset: Child<Button>, } impl Menu { @@ -43,7 +43,7 @@ impl Menu { let mut instance = Self { bg: Pad::with_background(BLD_BG), 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( Button::with_icon(Icon::new(X32)) diff --git a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs index a60f16c68f..a615a8b6a3 100644 --- a/core/embed/rust/src/ui/model_tt/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tt/bootloader/mod.rs @@ -74,8 +74,8 @@ impl ModelTTFeatures { let mut frame = ResultScreen::new( &RESULT_FW_INSTALL, Icon::new(CHECK40), - "Firmware installed\nsuccessfully", - Label::centered(msg, RESULT_FW_INSTALL.title_style()).vertically_centered(), + "Firmware installed\nsuccessfully".into(), + Label::centered(msg.into(), RESULT_FW_INSTALL.title_style()).vertically_centered(), complete_draw, ); show(&mut frame, complete_draw); @@ -85,8 +85,8 @@ impl ModelTTFeatures { let mut frame = ResultScreen::new( &RESULT_INITIAL, Icon::new(CHECK40), - "Firmware installed\nsuccessfully", - Label::centered(msg, RESULT_INITIAL.title_style()).vertically_centered(), + "Firmware installed\nsuccessfully".into(), + Label::centered(msg.into(), RESULT_INITIAL.title_style()).vertically_centered(), complete_draw, ); show(&mut frame, complete_draw); @@ -133,8 +133,8 @@ impl UIFeaturesBootloader for ModelTTFeatures { let mut frame = ResultScreen::new( &RESULT_FW_INSTALL, Icon::new(WARNING40), - "Firmware installation was not successful", - Label::centered(RECONNECT_MESSAGE, RESULT_FW_INSTALL.title_style()) + "Firmware installation was not successful".into(), + Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style()) .vertically_centered(), true, ); @@ -164,14 +164,16 @@ impl UIFeaturesBootloader for ModelTTFeatures { } else { "DOWNGRADE FW" }; - let title = Label::left_aligned(title_str, TEXT_BOLD).vertically_centered(); - let msg = Label::left_aligned(version_str.as_ref(), TEXT_NORMAL); - let alert = - (!should_keep_seed).then_some(Label::left_aligned("SEED WILL BE ERASED!", TEXT_BOLD)); + let title = Label::left_aligned(title_str.into(), TEXT_BOLD).vertically_centered(); + let msg = Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL); + let alert = (!should_keep_seed).then_some(Label::left_aligned( + "SEED WILL BE ERASED!".into(), + TEXT_BOLD, + )); let (left, right) = if should_keep_seed { - let l = Button::with_text("CANCEL").styled(button_bld()); - let r = Button::with_text("INSTALL").styled(button_confirm()); + let l = Button::with_text("CANCEL".into()).styled(button_bld()); + let r = Button::with_text("INSTALL".into()).styled(button_confirm()); (l, r) } else { let l = Button::with_icon(Icon::new(X24)).styled(button_bld()); @@ -180,7 +182,11 @@ impl UIFeaturesBootloader for ModelTTFeatures { }; let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg) - .with_info("FW FINGERPRINT", fingerprint, button_bld_menu()); + .with_info( + "FW FINGERPRINT".into(), + fingerprint.into(), + button_bld_menu(), + ); if let Some(alert) = alert { frame = frame.with_alert(alert); @@ -193,13 +199,13 @@ impl UIFeaturesBootloader for ModelTTFeatures { let icon = Icon::new(FIRE40); 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, ); - 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 left = Button::with_text("CANCEL").styled(button_wipe_cancel()); + let right = Button::with_text("RESET".into()).styled(button_wipe_confirm()); + let left = Button::with_text("CANCEL".into()).styled(button_wipe_cancel()); let mut frame = Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg) .with_alert(alert); @@ -230,7 +236,11 @@ impl UIFeaturesBootloader for ModelTTFeatures { unwrap!(version_str.push_str("\nby ")); 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) } @@ -288,8 +298,9 @@ impl UIFeaturesBootloader for ModelTTFeatures { let mut frame = ResultScreen::new( &RESULT_WIPE, Icon::new(CHECK40), - "Trezor reset\nsuccessfully", - Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(), + "Trezor reset\nsuccessfully".into(), + Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style()) + .vertically_centered(), true, ); show(&mut frame, true); @@ -299,8 +310,9 @@ impl UIFeaturesBootloader for ModelTTFeatures { let mut frame = ResultScreen::new( &RESULT_WIPE, Icon::new(WARNING40), - "Trezor reset was\nnot successful", - Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(), + "Trezor reset was\nnot successful".into(), + Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style()) + .vertically_centered(), true, ); show(&mut frame, true); diff --git a/core/embed/rust/src/ui/model_tt/component/address_details.rs b/core/embed/rust/src/ui/model_tt/component/address_details.rs index 4d6acd7a4e..49f67e5e86 100644 --- a/core/embed/rust/src/ui/model_tt/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tt/component/address_details.rs @@ -19,9 +19,9 @@ use super::{theme, Frame, FrameMsg}; const MAX_XPUBS: usize = 16; pub struct AddressDetails<T> { - qr_code: Frame<Qr, T>, - details: Frame<Paragraphs<ParagraphVecShort<'static>>, T>, - xpub_view: Frame<Paragraphs<Paragraph<'static>>, T>, + qr_code: Frame<Qr>, + details: Frame<Paragraphs<ParagraphVecShort<'static>>>, + xpub_view: Frame<Paragraphs<Paragraph<'static>>>, xpubs: Vec<(T, T), MAX_XPUBS>, xpub_page_count: Vec<u8, MAX_XPUBS>, current_page: usize, @@ -60,14 +60,14 @@ where let result = Self { qr_code: Frame::left_aligned( theme::label_title(), - qr_title, + qr_title.into(), Qr::new(qr_address, case_sensitive)?.with_border(7), ) .with_cancel_button() .with_border(theme::borders_horizontal_scroll()), details: Frame::left_aligned( theme::label_title(), - details_title, + details_title.into(), para.into_paragraphs(), ) .with_cancel_button() @@ -101,7 +101,7 @@ where // repaint after page change so we can use a dummy context here. let mut dummy_ctx = EventCtx::new(); 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| { p.inner_mut().update(self.xpubs[i].1.clone()); let npages = p.page_count(); diff --git a/core/embed/rust/src/ui/model_tt/component/bl_confirm.rs b/core/embed/rust/src/ui/model_tt/component/bl_confirm.rs index 0f79bb7907..9ad7f1517f 100644 --- a/core/embed/rust/src/ui/model_tt/component/bl_confirm.rs +++ b/core/embed/rust/src/ui/model_tt/component/bl_confirm.rs @@ -1,18 +1,22 @@ -use crate::ui::{ - component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad}, - constant, - constant::screen, - display::{Color, Icon}, - geometry::{Alignment2D, Insets, Offset, Point, Rect}, - model_tt::{ - component::{Button, ButtonMsg::Clicked, ButtonStyleSheet}, - constant::WIDTH, - theme::{ - bootloader::{ - text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING, - CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32, TITLE_AREA, X32, +use crate::{ + strutil::TString, + ui::{ + component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad}, + constant, + constant::screen, + display::{Color, Icon}, + geometry::{Alignment2D, Insets, Offset, Point, Rect}, + model_tt::{ + component::{Button, ButtonMsg::Clicked, ButtonStyleSheet}, + constant::WIDTH, + theme::{ + 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, } -pub enum ConfirmTitle<T> { - Text(Label<T>), +pub enum ConfirmTitle { + Text(Label<'static>), Icon(Icon), } -pub struct ConfirmInfo<T> { - pub title: Child<Label<T>>, - pub text: Child<Label<T>>, - pub info_button: Child<Button<&'static str>>, - pub close_button: Child<Button<&'static str>>, +pub struct ConfirmInfo<'a> { + pub title: Child<Label<'a>>, + pub text: Child<Label<'a>>, + pub info_button: Child<Button>, + pub close_button: Child<Button>, } -pub struct Confirm<T> { +pub struct Confirm<'a> { bg: Pad, content_pad: Pad, bg_color: Color, - title: ConfirmTitle<T>, - message: Child<Label<T>>, - alert: Option<Child<Label<T>>>, - left_button: Child<Button<T>>, - right_button: Child<Button<T>>, - info: Option<ConfirmInfo<T>>, + title: ConfirmTitle, + message: Child<Label<'a>>, + alert: Option<Child<Label<'static>>>, + left_button: Child<Button>, + right_button: Child<Button>, + info: Option<ConfirmInfo<'a>>, show_info: bool, } -impl<T> Confirm<T> -where - T: AsRef<str>, -{ +impl<'a> Confirm<'a> { pub fn new( bg_color: Color, - left_button: Button<T>, - right_button: Button<T>, - title: ConfirmTitle<T>, - message: Label<T>, + left_button: Button, + right_button: Button, + title: ConfirmTitle, + message: Label<'a>, ) -> Self { Self { 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 } - 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 { title: Child::new( Label::left_aligned(title, text_title(self.bg_color)).vertically_centered(), @@ -109,10 +115,7 @@ where } } -impl<T> Component for Confirm<T> -where - T: AsRef<str>, -{ +impl Component for Confirm<'_> { type Msg = ConfirmMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -242,10 +245,7 @@ where } #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Confirm<T> -where - T: AsRef<str>, -{ +impl crate::trace::Trace for Confirm<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("BlConfirm"); } diff --git a/core/embed/rust/src/ui/model_tt/component/button.rs b/core/embed/rust/src/ui/model_tt/component/button.rs index d9e2a558c2..fdf11ca15f 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -1,6 +1,7 @@ #[cfg(feature = "haptic")] use crate::trezorhal::haptic::{play, HapticEffect}; use crate::{ + strutil::TString, time::Duration, ui::{ component::{ @@ -21,22 +22,22 @@ pub enum ButtonMsg { LongPressed, } -pub struct Button<T> { +pub struct Button { area: Rect, touch_expand: Option<Insets>, - content: ButtonContent<T>, + content: ButtonContent, styles: ButtonStyleSheet, state: State, long_press: Option<Duration>, long_timer: Option<TimerToken>, } -impl<T> Button<T> { +impl Button { /// Offsets the baseline of the button text either up (negative) or down /// (positive). pub const BASELINE_OFFSET: i16 = -2; - pub const fn new(content: ButtonContent<T>) -> Self { + pub const fn new(content: ButtonContent) -> Self { Self { content, 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)) } @@ -117,17 +118,14 @@ impl<T> Button<T> { matches!(self.state, State::Disabled) } - pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent<T>) - where - T: PartialEq, - { + pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent) { if self.content != content { self.content = content; ctx.request_paint(); } } - pub fn content(&self) -> &ButtonContent<T> { + pub fn content(&self) -> &ButtonContent { &self.content } @@ -189,26 +187,24 @@ impl<T> Button<T> { } } - pub fn paint_content(&self, style: &ButtonStyle) - where - T: AsRef<str>, - { + pub fn paint_content(&self, style: &ButtonStyle) { match &self.content { ButtonContent::Empty => {} ButtonContent::Text(text) => { - let text = text.as_ref(); - let width = style.font.text_width(text); + let width = text.map(|c| style.font.text_width(c)); let height = style.font.text_height(); let start_of_baseline = self.area.center() + Offset::new(-width / 2, height / 2) + Offset::y(Self::BASELINE_OFFSET); - display::text_left( - start_of_baseline, - text, - style.font, - style.text_color, - style.button_color, - ); + text.map(|text| { + display::text_left( + start_of_baseline, + text, + style.font, + style.text_color, + style.button_color, + ); + }); } ButtonContent::Icon(icon) => { icon.draw( @@ -231,10 +227,7 @@ impl<T> Button<T> { } } -impl<T> Component for Button<T> -where - T: AsRef<str>, -{ +impl Component for Button { type Msg = ButtonMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -327,15 +320,12 @@ where } #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Button<T> -where - T: AsRef<str>, -{ +impl crate::trace::Trace for Button { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Button"); match &self.content { 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::IconAndText(content) => { t.string("text", content.text.into()); @@ -355,9 +345,9 @@ enum State { } #[derive(PartialEq, Eq)] -pub enum ButtonContent<T> { +pub enum ButtonContent { Empty, - Text(T), + Text(TString<'static>), Icon(Icon), IconAndText(IconText), IconBlend(Icon, Icon, Offset), @@ -381,19 +371,15 @@ pub struct ButtonStyle { pub border_width: i16, } -impl<T> Button<T> { +impl Button { pub fn cancel_confirm( - left: Button<T>, - right: Button<T>, + left: Button, + right: Button, left_is_small: bool, ) -> CancelConfirm< - T, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, - > - where - T: AsRef<str>, - { + > { let width = if left_is_small { theme::BUTTON_WIDTH } else { @@ -412,21 +398,17 @@ impl<T> Button<T> { } pub fn cancel_confirm_text( - left: Option<T>, - right: Option<T>, + left: Option<TString<'static>>, + right: Option<TString<'static>>, ) -> CancelConfirm< - T, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>, - > - where - T: AsRef<str>, - { + > { let left_is_small: bool; let left = if let Some(verb) = left { - left_is_small = verb.as_ref().len() <= 4; - if verb.as_ref() == "^" { + left_is_small = verb.len() <= 4; + if verb == "^".into() { Button::with_icon(theme::ICON_UP) } else { Button::with_text(verb) @@ -444,17 +426,13 @@ impl<T> Button<T> { } pub fn cancel_info_confirm( - confirm: T, - info: T, + confirm: TString<'static>, + info: TString<'static>, ) -> CancelInfoConfirm< - T, 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) .styled(theme::button_confirm()) .map(|msg| { @@ -479,16 +457,12 @@ impl<T> Button<T> { } pub fn select_word( - words: [T; 3], + words: [TString<'static>; 3], ) -> CancelInfoConfirm< - T, impl Fn(ButtonMsg) -> Option<SelectWordMsg>, impl Fn(ButtonMsg) -> Option<SelectWordMsg>, impl Fn(ButtonMsg) -> Option<SelectWordMsg>, - > - where - T: AsRef<str>, - { + > { let btn = move |i, word| { Button::with_text(word) .styled(theme::button_pin()) @@ -521,11 +495,10 @@ pub enum CancelConfirmMsg { Confirmed, } -type CancelInfoConfirm<T, F0, F1, F2> = FixedHeightBar< - Split<MsgMap<Button<T>, F0>, Split<MsgMap<Button<T>, F1>, MsgMap<Button<T>, F2>>>, ->; +type CancelInfoConfirm<F0, F1, 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)] pub enum CancelInfoConfirmMsg { diff --git a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs index 5a615328f7..7cb492d1d5 100644 --- a/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs +++ b/core/embed/rust/src/ui/model_tt/component/coinjoin_progress.rs @@ -3,7 +3,7 @@ use core::mem; use crate::{ error::Error, maybe_trace::MaybeTrace, - micropython::buffer::StrBuffer, + strutil::TString, translations::TR, ui::{ component::{ @@ -25,50 +25,44 @@ const LOADER_INNER: i16 = 28; const LOADER_OFFSET: i16 = -34; const LOADER_SPEED: u16 = 5; -pub struct CoinJoinProgress<T, U> { +pub struct CoinJoinProgress<U> { value: u16, 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. // To work around this, draw label every time loader is drawn. - label: Label<T>, + label: Label<'static>, } -impl<T, U> CoinJoinProgress<T, U> -where - T: AsRef<str>, -{ +impl<U> CoinJoinProgress<U> { pub fn new( - text: T, + text: TString<'static>, indeterminate: bool, - ) -> Result<CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>, Error> - where - T: AsRef<str>, - { + ) -> Result<CoinJoinProgress<impl Component<Msg = Never> + MaybeTrace>, Error> { let style = theme::label_coinjoin_progress(); - let label = Label::centered( - TryInto::<StrBuffer>::try_into(TR::coinjoin__title_do_not_disconnect)?, - style, - ) - .vertically_centered(); + let label = Label::centered(TR::coinjoin__title_do_not_disconnect.into(), style) + .vertically_centered(); let bg = painter::rect_painter(style.background_color, theme::BG); let inner = (bg, label); CoinJoinProgress::with_background(text, inner, indeterminate) } } -impl<T, U> CoinJoinProgress<T, U> +impl<U> CoinJoinProgress<U> where - T: AsRef<str>, 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 { value: 0, indeterminate, content: Frame::centered( theme::label_title(), - TR::coinjoin__title_progress.try_into()?, + TR::coinjoin__title_progress.into(), Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner), ) .into_child(), @@ -77,9 +71,8 @@ where } } -impl<T, U> Component for CoinJoinProgress<T, U> +impl<U> Component for CoinJoinProgress<U> where - T: AsRef<str>, U: Component<Msg = Never>, { type Msg = Never; @@ -132,9 +125,8 @@ where } #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for CoinJoinProgress<T, U> +impl<U> crate::trace::Trace for CoinJoinProgress<U> where - T: AsRef<str>, U: Component + crate::trace::Trace, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { diff --git a/core/embed/rust/src/ui/model_tt/component/error.rs b/core/embed/rust/src/ui/model_tt/component/error.rs index 739770f4f4..3769f33c9f 100644 --- a/core/embed/rust/src/ui/model_tt/component/error.rs +++ b/core/embed/rust/src/ui/model_tt/component/error.rs @@ -1,7 +1,10 @@ -use crate::ui::{ - component::{Child, Component, Event, EventCtx, Label, Never, Pad}, - constant::screen, - geometry::{Alignment2D, Point, Rect}, +use crate::{ + strutil::TString, + ui::{ + component::{Child, Component, Event, EventCtx, Label, Never, Pad}, + constant::screen, + geometry::{Alignment2D, Point, Rect}, + }, }; use crate::ui::model_tt::{ @@ -19,15 +22,15 @@ const STYLE: &ResultStyle = &crate::ui::model_tt::theme::bootloader::RESULT_WIPE #[cfg(not(feature = "bootloader"))] const STYLE: &ResultStyle = &super::theme::RESULT_ERROR; -pub struct ErrorScreen<'a, T> { +pub struct ErrorScreen<'a> { bg: Pad, - title: Child<Label<T>>, - message: Child<Label<T>>, - footer: Child<ResultFooter<'a, T>>, + title: Child<Label<'a>>, + message: Child<Label<'a>>, + footer: Child<ResultFooter<'a>>, } -impl<T: AsRef<str>> ErrorScreen<'_, T> { - pub fn new(title: T, message: T, footer: T) -> Self { +impl<'a> ErrorScreen<'a> { + pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self { let title = Label::centered(title, STYLE.title_style()); let message = Label::centered(message, STYLE.message_style()); 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; fn place(&mut self, _bounds: Rect) -> Rect { @@ -62,7 +65,7 @@ impl<T: AsRef<str>> Component for ErrorScreen<'_, T> { ); self.message.place(message_area); - let (_, bottom_area) = ResultFooter::<T>::split_bounds(); + let (_, bottom_area) = ResultFooter::split_bounds(); self.footer.place(bottom_area); screen() diff --git a/core/embed/rust/src/ui/model_tt/component/fido.rs b/core/embed/rust/src/ui/model_tt/component/fido.rs index 3b7f64caaf..4a1b98e501 100644 --- a/core/embed/rust/src/ui/model_tt/component/fido.rs +++ b/core/embed/rust/src/ui/model_tt/component/fido.rs @@ -1,11 +1,14 @@ -use crate::ui::{ - component::{image::Image, Child, Component, Event, EventCtx, Label}, - display, - geometry::{Insets, Rect}, - model_tt::component::{ - fido_icons::get_fido_icon_data, - swipe::{Swipe, SwipeDirection}, - theme, ScrollBar, +use crate::{ + strutil::TString, + ui::{ + component::{image::Image, Child, Component, Event, EventCtx, Label}, + display, + geometry::{Insets, Rect}, + model_tt::component::{ + fido_icons::get_fido_icon_data, + swipe::{Swipe, SwipeDirection}, + theme, ScrollBar, + }, }, }; @@ -23,10 +26,10 @@ pub enum FidoMsg { Cancelled, } -pub struct FidoConfirm<F: Fn(usize) -> T, T, U> { +pub struct FidoConfirm<F: Fn(usize) -> TString<'static>, U> { page_swipe: Swipe, - app_name: Label<T>, - account_name: Label<T>, + app_name: Label<'static>, + account_name: Label<'static>, icon: Child<Image>, /// Function/closure that will return appropriate page on demand. get_account: F, @@ -35,20 +38,19 @@ pub struct FidoConfirm<F: Fn(usize) -> T, T, U> { controls: U, } -impl<F, T, U> FidoConfirm<F, T, U> +impl<F, U> FidoConfirm<F, U> where - F: Fn(usize) -> T, - T: AsRef<str> + From<&'static str>, + F: Fn(usize) -> TString<'static>, U: Component<Msg = CancelConfirmMsg>, { pub fn new( - app_name: T, + app_name: TString<'static>, get_account: F, page_count: usize, - icon_name: Option<T>, + icon_name: Option<TString<'static>>, controls: U, ) -> 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. let mut scrollbar = ScrollBar::horizontal(); @@ -116,10 +118,9 @@ where } } -impl<F, T, U> Component for FidoConfirm<F, T, U> +impl<F, U> Component for FidoConfirm<F, U> where - F: Fn(usize) -> T, - T: AsRef<str> + From<&'static str>, + F: Fn(usize) -> TString<'static>, U: Component<Msg = CancelConfirmMsg>, { type Msg = FidoMsg; @@ -198,8 +199,8 @@ where // Account name is optional. // Showing it only if it differs from app name. // (Dummy requests usually have some text as both app_name and account_name.) - let account_name = self.account_name.text().as_ref(); - let app_name = self.app_name.text().as_ref(); + let account_name = self.account_name.text(); + let app_name = self.app_name.text(); if !account_name.is_empty() && account_name != app_name { self.account_name.paint(); } @@ -219,9 +220,9 @@ where } #[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 - F: Fn(usize) -> T, + F: Fn(usize) -> TString<'static>, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("FidoConfirm"); diff --git a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs index df39e17a65..eff1f63950 100644 --- a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs +++ b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs @@ -3,6 +3,9 @@ //! do not edit manually! +use crate::strutil::TString; + + 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_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. /// Returns default `ICON_WEBAUTHN` when the icon is not found or name not /// 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 { - match icon_name.as_ref() { + icon_name.map(|c| match c { "apple" => ICON_APPLE, "aws" => ICON_AWS, "binance" => ICON_BINANCE, @@ -73,7 +76,7 @@ pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] "stripe" => ICON_STRIPE, "tutanota" => ICON_TUTANOTA, _ => ICON_WEBAUTHN, - } + }) } else { ICON_WEBAUTHN } diff --git a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako index cca05ce390..6d76554ad5 100644 --- a/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako +++ b/core/embed/rust/src/ui/model_tt/component/fido_icons.rs.mako @@ -2,6 +2,9 @@ //! (by running `make templates` in `core`) //! do not edit manually! + +use crate::strutil::TString; + <% icons: list[tuple[str, str]] = [] for app in fido: @@ -21,14 +24,14 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif" /// Translates icon name into its data. /// Returns default `ICON_WEBAUTHN` when the icon is not found or name not /// 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 { - match icon_name.as_ref() { + icon_name.map(|c| match c { % for icon_name, var_name in icons: "${icon_name}" => ICON_${var_name}, % endfor _ => ICON_WEBAUTHN, - } + }) } else { ICON_WEBAUTHN } diff --git a/core/embed/rust/src/ui/model_tt/component/frame.rs b/core/embed/rust/src/ui/model_tt/component/frame.rs index ebd5aae12e..480350dfee 100644 --- a/core/embed/rust/src/ui/model_tt/component/frame.rs +++ b/core/embed/rust/src/ui/model_tt/component/frame.rs @@ -1,18 +1,21 @@ use super::theme; -use crate::ui::{ - component::{ - base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx, +use crate::{ + strutil::TString, + 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, - title: Child<Label<U>>, - subtitle: Option<Child<Label<U>>>, - button: Option<Child<Button<&'static str>>>, + title: Child<Label<'static>>, + subtitle: Option<Child<Label<'static>>>, + button: Option<Child<Button>>, button_msg: CancelInfoConfirmMsg, content: Child<T>, } @@ -22,12 +25,16 @@ pub enum FrameMsg<T> { Button(CancelInfoConfirmMsg), } -impl<T, U> Frame<T, U> +impl<T> Frame<T> where 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 { title: Child::new(Label::new(title, alignment, style)), 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) } - 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) } - 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) } @@ -55,7 +62,7 @@ where 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( subtitle, self.title.inner().alignment(), @@ -91,7 +98,7 @@ where 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| { t.set_text(new_title); 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 T: Component, - U: AsRef<str>, { type Msg = FrameMsg<T::Msg>; @@ -179,10 +185,9 @@ where } #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for Frame<T, U> +impl<T> crate::trace::Trace for Frame<T> where T: crate::trace::Trace, - U: AsRef<str>, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Frame"); diff --git a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs index ed188815e3..1033139f5d 100644 --- a/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs +++ b/core/embed/rust/src/ui/model_tt/component/homescreen/mod.rs @@ -269,14 +269,14 @@ impl crate::trace::Trace for Homescreen { } } -pub struct Lockscreen { - label: TString<'static>, +pub struct Lockscreen<'a> { + label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool, } -impl Lockscreen { - pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self { +impl<'a> Lockscreen<'a> { + pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self { Lockscreen { label, bootscreen, @@ -285,7 +285,7 @@ impl Lockscreen { } } -impl Component for Lockscreen { +impl Component for Lockscreen<'_> { type Msg = HomescreenMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -401,7 +401,7 @@ fn is_image_toif(buffer: &[u8]) -> bool { } #[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) { t.component("Lockscreen"); } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs index fb8025695c..0415d985c2 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs @@ -21,9 +21,9 @@ use heapless::String; const MAX_LENGTH: usize = 8; pub struct Bip39Input { - button: Button<&'static str>, + button: Button, // used only to keep track of suggestion text color - button_suggestion: Button<&'static str>, + button_suggestion: Button, textbox: TextBox<MAX_LENGTH>, multi_tap: MultiTapKeyboard, options_num: Option<usize>, @@ -263,7 +263,7 @@ impl Bip39Input { // Disabled button. self.button.disable(ctx); self.button.set_stylesheet(ctx, theme::button_pin()); - self.button.set_content(ctx, ButtonContent::Text("")); + self.button.set_content(ctx, ButtonContent::Text("".into())); } } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs index 0ca86598d6..03f9c23155 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs @@ -1,9 +1,12 @@ -use crate::ui::{ - component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe}, - geometry::{Alignment2D, Grid, Offset, Rect}, - model_tt::{ - component::{Button, ButtonMsg, Swipe, SwipeDirection}, - theme, +use crate::{ + strutil::TString, + ui::{ + component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe}, + geometry::{Alignment2D, Grid, Offset, Rect}, + model_tt::{ + component::{Button, ButtonMsg, Swipe, SwipeDirection}, + theme, + }, }, }; @@ -14,27 +17,26 @@ pub enum MnemonicKeyboardMsg { Previous, } -pub struct MnemonicKeyboard<T, U> { +pub struct MnemonicKeyboard<T> { /// Initial prompt, displayed on empty input. - prompt: Child<Maybe<Label<U>>>, + prompt: Child<Maybe<Label<'static>>>, /// Backspace button. - back: Child<Maybe<Button<&'static str>>>, + back: Child<Maybe<Button>>, /// Input area, acting as the auto-complete and confirm button. input: Child<Maybe<T>>, /// 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: Swipe, /// Whether going back is allowed (is not on the very first word). can_go_back: bool, } -impl<T, U> MnemonicKeyboard<T, U> +impl<T> MnemonicKeyboard<T> where 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 let prompt_visible = input.is_empty(); @@ -57,7 +59,7 @@ where )), input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)), 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), swipe: Swipe::new().right(), can_go_back, @@ -99,10 +101,9 @@ where } } -impl<T, U> Component for MnemonicKeyboard<T, U> +impl<T> Component for MnemonicKeyboard<T> where T: MnemonicInput, - U: AsRef<str>, { type Msg = MnemonicKeyboardMsg; @@ -210,10 +211,9 @@ pub enum MnemonicInputMsg { } #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for MnemonicKeyboard<T, U> +impl<T> crate::trace::Trace for MnemonicKeyboard<T> where T: MnemonicInput + crate::trace::Trace, - U: AsRef<str>, { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("MnemonicKeyboard"); diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs index ab74d7a698..01e315a999 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs @@ -1,16 +1,19 @@ -use crate::ui::{ - component::{ - base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never, +use crate::{ + strutil::TString, + 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, }; use core::cell::Cell; @@ -23,9 +26,9 @@ pub enum PassphraseKeyboardMsg { pub struct PassphraseKeyboard { page_swipe: Swipe, input: Child<Input>, - back: Child<Button<&'static str>>, - confirm: Child<Button<&'static str>>, - keys: [Child<Button<&'static str>>; KEY_COUNT], + back: Child<Button>, + confirm: Child<Button>, + keys: [Child<Button>; KEY_COUNT], scrollbar: ScrollBar, fade: Cell<bool>, } @@ -69,20 +72,20 @@ impl PassphraseKeyboard { } } - fn key_text(content: &ButtonContent<&'static str>) -> &'static str { + fn key_text(content: &ButtonContent) -> TString<'static> { match content { - ButtonContent::Text(text) => text, - ButtonContent::Icon(_) => " ", - ButtonContent::IconAndText(_) => " ", - ButtonContent::Empty => "", - ButtonContent::IconBlend(_, _, _) => "", + ButtonContent::Text(text) => *text, + ButtonContent::Icon(_) => " ".into(), + ButtonContent::IconAndText(_) => " ".into(), + ButtonContent::Empty => "".into(), + ButtonContent::IconBlend(_, _, _) => "".into(), } } - fn key_content(text: &'static str) -> ButtonContent<&'static str> { + fn key_content(text: &'static str) -> ButtonContent { match text { " " => ButtonContent::Icon(theme::ICON_SPACE), - t => ButtonContent::Text(t), + t => ButtonContent::Text(t.into()), } } @@ -272,7 +275,7 @@ impl Component for PassphraseKeyboard { // character in textbox. If not, let's just append the first character. let text = Self::key_text(btn.inner().content()); self.input.mutate(ctx, |ctx, i| { - let edit = i.multi_tap.click_key(ctx, key, text); + let edit = text.map(|c| i.multi_tap.click_key(ctx, key, c)); i.textbox.apply(ctx, edit); }); self.after_edit(ctx); diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs index d45e4c4732..7c5a74c73a 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs @@ -2,6 +2,7 @@ use core::mem; use heapless::String; use crate::{ + strutil::TString, time::Duration, trezorhal::random, ui::{ @@ -39,32 +40,29 @@ const HEADER_PADDING: Insets = Insets::new( HEADER_PADDING_SIDE, ); -pub struct PinKeyboard<T> { +pub struct PinKeyboard<'a> { allow_cancel: bool, - major_prompt: Child<Label<T>>, - minor_prompt: Child<Label<T>>, - major_warning: Option<Child<Label<T>>>, + major_prompt: Child<Label<'a>>, + minor_prompt: Child<Label<'a>>, + major_warning: Option<Child<Label<'a>>>, textbox: Child<PinDots>, textbox_pad: Pad, - erase_btn: Child<Maybe<Button<&'static str>>>, - cancel_btn: Child<Maybe<Button<&'static str>>>, - confirm_btn: Child<Button<&'static str>>, - digit_btns: [Child<Button<&'static str>>; DIGIT_COUNT], + erase_btn: Child<Maybe<Button>>, + cancel_btn: Child<Maybe<Button>>, + confirm_btn: Child<Button>, + digit_btns: [Child<Button>; DIGIT_COUNT], warning_timer: Option<TimerToken>, } -impl<T> PinKeyboard<T> -where - T: AsRef<str>, -{ +impl<'a> PinKeyboard<'a> { // Label position fine-tuning. const MAJOR_OFF: Offset = Offset::y(11); const MINOR_OFF: Offset = Offset::y(11); pub fn new( - major_prompt: T, - minor_prompt: T, - major_warning: Option<T>, + major_prompt: TString<'a>, + minor_prompt: TString<'a>, + major_warning: Option<TString<'a>>, allow_cancel: bool, ) -> Self { // 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. let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; random::shuffle(&mut digits); digits - .map(Button::with_text) + .map(|c| Button::with_text(c.into())) .map(|b| b.styled(theme::button_pin())) .map(Child::new) } @@ -146,10 +144,7 @@ where } } -impl<T> Component for PinKeyboard<T> -where - T: AsRef<str>, -{ +impl Component for PinKeyboard<'_> { type Msg = PinKeyboardMsg; fn place(&mut self, bounds: Rect) -> Rect { @@ -238,7 +233,9 @@ where for btn in &mut self.digit_btns { if let Some(Clicked) = btn.event(ctx, event) { if let ButtonContent::Text(text) = btn.inner().content() { - self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text)); + text.map(|text| { + self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text)); + }); self.pin_modified(ctx); return None; } @@ -460,18 +457,18 @@ impl Component for PinDots { } #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for PinKeyboard<T> -where - T: AsRef<str>, -{ +impl crate::trace::Trace for PinKeyboard<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("PinKeyboard"); // So that debuglink knows the locations of the buttons let mut digits_order: String<10> = String::new(); for btn in self.digit_btns.iter() { let btn_content = btn.inner().content(); + if let ButtonContent::Text(text) = btn_content { - unwrap!(digits_order.push_str(text)); + text.map(|text| { + unwrap!(digits_order.push_str(text)); + }); } } t.string("digits_order", digits_order.as_str().into()); diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs index 3f927392bd..54aa9a702b 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs @@ -28,7 +28,7 @@ use crate::{ const MAX_LENGTH: usize = 8; pub struct Slip39Input { - button: Button<&'static str>, + button: Button, textbox: TextBox<MAX_LENGTH>, multi_tap: MultiTapKeyboard, final_word: Option<&'static str>, @@ -293,7 +293,7 @@ impl Slip39Input { } else { // Disabled button. self.button.disable(ctx); - self.button.set_content(ctx, ButtonContent::Text("")); + self.button.set_content(ctx, ButtonContent::Text("".into())); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs index 8202a03fad..453c809381 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs @@ -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)]; pub struct SelectWordCount { - button: [Button<&'static str>; NUMBERS.len()], + button: [Button; NUMBERS.len()], } pub enum SelectWordCountMsg { @@ -22,7 +22,7 @@ pub enum SelectWordCountMsg { impl SelectWordCount { pub fn new() -> Self { 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())), } } } diff --git a/core/embed/rust/src/ui/model_tt/component/number_input.rs b/core/embed/rust/src/ui/model_tt/component/number_input.rs index 702cc156fa..8aa6f14b14 100644 --- a/core/embed/rust/src/ui/model_tt/component/number_input.rs +++ b/core/embed/rust/src/ui/model_tt/component/number_input.rs @@ -1,6 +1,5 @@ use crate::{ error::Error, - micropython::buffer::StrBuffer, strutil::{self, StringType}, translations::TR, ui::{ @@ -31,8 +30,8 @@ where input: Child<NumberInput>, paragraphs: Child<Paragraphs<Paragraph<'static>>>, paragraphs_pad: Pad, - info_button: Child<Button<StrBuffer>>, - confirm_button: Child<Button<StrBuffer>>, + info_button: Child<Button>, + confirm_button: Child<Button>, } impl<T, F> NumberInputDialog<T, F> @@ -48,8 +47,8 @@ where input: NumberInput::new(min, max, init_value).into_child(), paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(), paragraphs_pad: Pad::with_background(theme::BG), - info_button: Button::with_text(TR::buttons__info.try_into()?).into_child(), - confirm_button: Button::with_text(TR::buttons__continue.try_into()?) + info_button: Button::with_text(TR::buttons__info.into()).into_child(), + confirm_button: Button::with_text(TR::buttons__continue.into()) .styled(theme::button_confirm()) .into_child(), }) @@ -154,8 +153,8 @@ pub enum NumberInputMsg { pub struct NumberInput { area: Rect, - dec: Child<Button<&'static str>>, - inc: Child<Button<&'static str>>, + dec: Child<Button>, + inc: Child<Button>, min: u32, max: u32, value: u32, @@ -163,10 +162,10 @@ pub struct NumberInput { impl NumberInput { 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()) .into_child(); - let inc = Button::with_text("+") + let inc = Button::with_text("+".into()) .styled(theme::button_counter()) .into_child(); let value = value.clamp(min, max); @@ -217,7 +216,7 @@ impl Component for NumberInput { let mut buf = [0u8; 10]; if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) { 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::text_center( self.area.center() + Offset::y(y_offset), diff --git a/core/embed/rust/src/ui/model_tt/component/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index 34f9213fc8..c8fa2756b6 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -1,6 +1,6 @@ use crate::{ error::Error, - micropython::buffer::StrBuffer, + strutil::TString, time::Instant, translations::TR, ui::{ @@ -19,7 +19,7 @@ use super::{ /// Allows pagination of inner component. Shows scroll bar, confirm & cancel /// buttons. Optionally handles hold-to-confirm with loader. -pub struct ButtonPage<T, U> { +pub struct ButtonPage<T> { /// Inner component. content: T, /// Cleared when page changes. @@ -29,10 +29,10 @@ pub struct ButtonPage<T, U> { scrollbar: ScrollBar, /// Hold-to-confirm mode whenever this is `Some(loader)`. loader: Option<Loader>, - button_cancel: Option<Button<U>>, - button_confirm: Button<U>, - button_prev: Button<&'static str>, - button_next: Button<&'static str>, + button_cancel: Option<Button>, + button_confirm: Button, + button_prev: Button, + button_next: Button, /// Show cancel button instead of back button. cancel_from_any_page: bool, /// Whether to pass-through left swipe to parent component. @@ -43,24 +43,23 @@ pub struct ButtonPage<T, U> { fade: Option<u16>, } -impl<T> ButtonPage<T, StrBuffer> +impl<T> ButtonPage<T> where T: Paginate, T: Component, { pub fn with_hold(mut self) -> Result<Self, Error> { - self.button_confirm = Button::with_text(TR::buttons__hold_to_confirm.try_into()?) - .styled(theme::button_confirm()); + self.button_confirm = + Button::with_text(TR::buttons__hold_to_confirm.into()).styled(theme::button_confirm()); self.loader = Some(Loader::new()); Ok(self) } } -impl<T, U> ButtonPage<T, U> +impl<T> ButtonPage<T> where T: Paginate, T: Component, - U: AsRef<str> + From<&'static str>, { pub fn new(content: T, background: Color) -> Self { Self { @@ -85,13 +84,17 @@ where 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 { - Some(verb) => match verb.as_ref() { + Some(verb) => verb.map(|s| match s { "^" => Button::with_icon(theme::ICON_UP), "<" => Button::with_icon(theme::ICON_BACK), _ => Button::with_text(verb), - }, + }), _ => Button::with_icon(theme::ICON_CANCEL), }; let confirm = match right { @@ -282,11 +285,10 @@ enum HandleResult<T> { Continue, } -impl<T, U> Component for ButtonPage<T, U> +impl<T> Component for ButtonPage<T> where T: Paginate, T: Component, - U: AsRef<str> + From<&'static str>, { type Msg = PageMsg<T::Msg>; @@ -294,7 +296,7 @@ where let small_left_button = match (&self.button_cancel, &self.button_confirm) { (None, _) => true, (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, _ => true, }, @@ -425,7 +427,7 @@ where } #[cfg(feature = "ui_debug")] -impl<T, U> crate::trace::Trace for ButtonPage<T, U> +impl<T> crate::trace::Trace for ButtonPage<T> where T: crate::trace::Trace, { @@ -528,10 +530,7 @@ mod tests { #[test] fn paragraphs_empty() { - let mut page = ButtonPage::<_, &'static str>::new( - Paragraphs::<[Paragraph<'static>; 0]>::new([]), - theme::BG, - ); + let mut page = ButtonPage::new(Paragraphs::<[Paragraph<'static>; 0]>::new([]), theme::BG); page.place(SCREEN); let expected = serde_json::json!({ @@ -554,7 +553,7 @@ mod tests { #[test] fn paragraphs_single() { - let mut page = ButtonPage::<_, &'static str>::new( + let mut page = ButtonPage::new( Paragraphs::new([ Paragraph::new( &theme::TEXT_NORMAL, @@ -592,7 +591,7 @@ mod tests { #[test] fn paragraphs_one_long() { - let mut page = ButtonPage::<_, &'static str>::new( + let mut page = ButtonPage::new( Paragraphs::new( Paragraph::new( &theme::TEXT_BOLD, @@ -650,7 +649,7 @@ mod tests { #[test] fn paragraphs_three_long() { - let mut page = ButtonPage::<_, &'static str>::new( + let mut page = ButtonPage::new( Paragraphs::new([ Paragraph::new( &theme::TEXT_BOLD, @@ -750,7 +749,7 @@ mod tests { #[test] fn paragraphs_hard_break() { - let mut page = ButtonPage::<_, &'static str>::new( + let mut page = ButtonPage::new( Paragraphs::new([ Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(), Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(), diff --git a/core/embed/rust/src/ui/model_tt/component/progress.rs b/core/embed/rust/src/ui/model_tt/component/progress.rs index ad6d8dd561..048bbc40a0 100644 --- a/core/embed/rust/src/ui/model_tt/component/progress.rs +++ b/core/embed/rust/src/ui/model_tt/component/progress.rs @@ -1,7 +1,7 @@ use core::mem; use crate::{ - strutil::{StringType, TString}, + strutil::TString, ui::{ component::{ base::ComponentExt, @@ -18,8 +18,8 @@ use crate::{ use super::theme; -pub struct Progress<T> { - title: Child<Label<T>>, +pub struct Progress { + title: Child<Label<'static>>, value: u16, loader_y_offset: i16, indeterminate: bool, @@ -27,13 +27,14 @@ pub struct Progress<T> { description_pad: Pad, } -impl<T> Progress<T> -where - T: StringType, -{ +impl Progress { 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 { title: Label::centered(title, theme::label_progress()).into_child(), value: 0, @@ -48,10 +49,7 @@ where } } -impl<T> Component for Progress<T> -where - T: StringType, -{ +impl Component for Progress { type Msg = Never; fn place(&mut self, _bounds: Rect) -> Rect { @@ -117,10 +115,7 @@ where } #[cfg(feature = "ui_debug")] -impl<T> crate::trace::Trace for Progress<T> -where - T: StringType, -{ +impl crate::trace::Trace for Progress { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Progress"); } diff --git a/core/embed/rust/src/ui/model_tt/component/result.rs b/core/embed/rust/src/ui/model_tt/component/result.rs index 26bffb84f6..f5bb2f2151 100644 --- a/core/embed/rust/src/ui/model_tt/component/result.rs +++ b/core/embed/rust/src/ui/model_tt/component/result.rs @@ -1,5 +1,5 @@ use crate::{ - strutil::StringType, + strutil::TString, ui::{ component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad}, constant::screen, @@ -41,21 +41,23 @@ impl ResultStyle { } } -pub struct ResultFooter<'a, T> { +pub struct ResultFooter<'a> { style: &'a ResultStyle, - text: Label<T>, + text: Label<'a>, area: Rect, } -impl<'a, T: AsRef<str>> ResultFooter<'a, T> { - pub fn new(text: Label<T>, style: &'a ResultStyle) -> Self { +impl<'a> ResultFooter<'a> { + pub fn new(text: Label<'a>, style: &'a ResultStyle) -> Self { Self { style, text, area: Rect::zero(), } } +} +impl ResultFooter<'_> { pub const fn split_bounds() -> (Rect, Rect) { let main_area = Rect::new( Point::new(RESULT_PADDING, 0), @@ -69,7 +71,7 @@ impl<'a, T: AsRef<str>> ResultFooter<'a, T> { } } -impl<T: AsRef<str>> Component for ResultFooter<'_, T> { +impl Component for ResultFooter<'_> { type Msg = Never; fn place(&mut self, bounds: Rect) -> Rect { @@ -78,6 +80,10 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> { bounds } + fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> { + None + } + fn paint(&mut self) { // divider line let bar = Rect::from_center_and_size( @@ -89,27 +95,23 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> { // footer text 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, footer_pad: Pad, style: &'a ResultStyle, icon: Icon, - message: Child<Label<T>>, - footer: Child<ResultFooter<'a, &'a str>>, + message: Child<Label<'a>>, + footer: Child<ResultFooter<'a>>, } -impl<'a, T: StringType> ResultScreen<'a, T> { +impl<'a> ResultScreen<'a> { pub fn new( style: &'a ResultStyle, icon: Icon, - message: T, - footer: Label<&'a str>, + message: TString<'a>, + footer: Label<'a>, complete_draw: bool, ) -> Self { let mut instance = Self { @@ -130,13 +132,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; fn place(&mut self, _bounds: Rect) -> Rect { self.bg.place(screen()); - let (main_area, footer_area) = ResultFooter::<&'a str>::split_bounds(); + let (main_area, footer_area) = ResultFooter::split_bounds(); self.footer_pad.place(footer_area); self.footer.place(footer_area); diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index c7e5a075c5..f7fa1d0e37 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -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 - F: Fn(usize) -> T, - T: StringType, + F: Fn(usize) -> TString<'static>, U: Component<Msg = CancelConfirmMsg>, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { @@ -141,10 +140,7 @@ where } } -impl<T> ComponentMsgObj for PinKeyboard<T> -where - T: AsRef<str>, -{ +impl ComponentMsgObj for PinKeyboard<'_> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { 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 T: MnemonicInput, - U: AsRef<str>, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { @@ -181,10 +176,9 @@ where } } -impl<T, U> ComponentMsgObj for Frame<T, U> +impl<T> ComponentMsgObj for Frame<T> where T: ComponentMsgObj, - U: AsRef<str>, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { @@ -194,10 +188,9 @@ where } } -impl<T, U> ComponentMsgObj for ButtonPage<T, U> +impl<T> ComponentMsgObj for ButtonPage<T> where T: Component + Paginate, - U: AsRef<str> + From<&'static str>, { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { @@ -269,10 +262,7 @@ where } } -impl<T> ComponentMsgObj for Progress<T> -where - T: StringType, -{ +impl ComponentMsgObj for Progress { fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> { 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> { match msg { 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 - T: AsRef<str>, U: Component<Msg = Never>, { 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> -where - T: AsRef<str>, -{ +impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> { match msg { 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 { ButtonPage::new(paragraphs, theme::BG).with_hold()? } 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 { 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()) }; 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( theme::label_title(), - title, + title.into(), 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()) }; @@ -530,14 +521,14 @@ impl ConfirmBlobParams { let mut page = ButtonPage::new(paragraphs, theme::BG); 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 { 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 { - frame = frame.with_subtitle(theme::label_subtitle(), subtitle); + frame = frame.with_subtitle(theme::label_subtitle(), subtitle.into()); } if self.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( Frame::left_aligned( theme::label_title(), - title, + title.into(), ButtonPage::new(paragraphs, theme::BG) .with_swipe_left() - .with_cancel_confirm(None, Some(verb)), + .with_cancel_confirm(None, Some(verb.into())), ) .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, )?; - let page: ButtonPage<_, StrBuffer> = if hold { + let page = if hold { ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()? } else { 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.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()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -665,11 +660,10 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m _ => return Err(value_error!("Invalid image.")), }; - 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::buttons__change.into())); let obj = LayoutObj::new(Frame::centered( theme::label_title(), - title, + title.into(), Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons), ))?; Ok(obj.into()) @@ -691,12 +685,12 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: let paragraphs = Paragraphs::new(par_array); let buttons = Button::cancel_confirm( Button::with_icon(theme::ICON_CANCEL), - Button::with_text(button).styled(theme::button_confirm()), + Button::with_text(button.into()).styled(theme::button_confirm()), true, ); let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), Dialog::new(paragraphs, buttons), ))?; Ok(obj.into()) @@ -768,7 +762,7 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs: let obj = LayoutObj::new( Frame::left_aligned( theme::label_title(), - title, + title.into(), SimplePage::new(paragraphs.into_paragraphs(), axis, theme::BG) .with_swipe_right_to_go_back(), ) @@ -824,15 +818,14 @@ 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_MONO, value)); } - let mut page: ButtonPage<_, StrBuffer> = - ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?; + let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?; if cancel_arrow { page = page.with_cancel_arrow() } if info_button { 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 { frame = frame.with_info_button(); } @@ -861,12 +854,11 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs: Paragraph::new(&theme::TEXT_MONO, amount_new), ]); - let tr_title: StrBuffer = TR::modify_amount__title.try_into()?; let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - tr_title, - ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG) - .with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.try_into()?)), + TR::modify_amount__title.into(), + ButtonPage::new(paragraphs, theme::BG) + .with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.into())), ))?; Ok(obj.into()) }; @@ -908,8 +900,8 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m let obj = LayoutObj::new( Frame::left_aligned( theme::label_title(), - title, - ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG) + title.into(), + ButtonPage::new(paragraphs, theme::BG) .with_hold()? .with_swipe_left(), ) @@ -963,7 +955,7 @@ fn new_show_modal( title, Button::cancel_confirm( Button::with_icon(theme::ICON_CANCEL), - Button::with_text(button).styled(button_style), + Button::with_text(button.into()).styled(button_style), false, ), ) @@ -977,9 +969,9 @@ fn new_show_modal( IconDialog::new( icon, title, - theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| { - (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) - })), + theme::button_bar(Button::with_text(button.into()).styled(button_style).map( + |msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed), + )), ) .with_value(value) .with_description(description), @@ -1008,7 +1000,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map let block = move |_args: &[Obj], kwargs: &Map| { 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 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()?; // Cache the page count so that we can move `accounts` into the closure. @@ -1023,14 +1015,17 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map let controls = Button::cancel_confirm( Button::with_icon(theme::ICON_CANCEL), - Button::<StrBuffer>::with_text(TR::buttons__confirm.try_into()?) - .styled(theme::button_confirm()), + Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()), 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()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1098,7 +1093,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma title, Button::cancel_confirm( Button::with_icon(theme::ICON_BACK), - Button::with_text(button).styled(theme::button_reset()), + Button::with_text(button.into()).styled(theme::button_reset()), true, ), ) @@ -1128,10 +1123,10 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) let obj = if let Some(t) = title { LayoutObj::new(Frame::left_aligned( theme::label_title(), - t, + t.into(), Dialog::new( 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) })), ), @@ -1142,7 +1137,7 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map) theme::borders(), Dialog::new( 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) })), ), @@ -1185,11 +1180,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( theme::label_title(), - title, + title.into(), Dialog::new(paragraphs.into_paragraphs(), buttons), ))?; Ok(obj.into()) @@ -1214,9 +1209,9 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), 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_back_button(), ))?; @@ -1237,11 +1232,10 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut Paragraph::new(&theme::TEXT_MONO, max_feerate), ]); - let tr_title: StrBuffer = TR::coinjoin__title.try_into()?; let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - tr_title, - ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold()?, + TR::coinjoin__title.into(), + ButtonPage::new(paragraphs, theme::BG).with_hold()?, ))?; Ok(obj.into()) }; @@ -1255,11 +1249,16 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?; let warning: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?; let warning = if warning { - Some(TR::pin__wrong_pin.try_into()?) + Some(TR::pin__wrong_pin.into()) } else { 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()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1282,7 +1281,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 obj = LayoutObj::new(MnemonicKeyboard::new( Bip39Input::prefilled_word(prefill_word.as_ref()), - prompt, + prompt.into(), can_go_back, ))?; Ok(obj.into()) @@ -1297,7 +1296,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 obj = LayoutObj::new(MnemonicKeyboard::new( Slip39Input::prefilled_word(prefill_word.as_ref()), - prompt, + prompt.into(), can_go_back, ))?; Ok(obj.into()) @@ -1310,12 +1309,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 description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; 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 obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), Dialog::new(paragraphs, Button::select_word(words)), ))?; Ok(obj.into()) @@ -1336,8 +1335,8 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, - ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG) + title.into(), + ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) .with_hold()? .without_cancel(), ))?; @@ -1366,7 +1365,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), NumberInputDialog::new(min_count, max_count, count, callback)?, ))?; Ok(obj.into()) @@ -1394,7 +1393,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), Dialog::new( Checklist::from_paragraphs( theme::ICON_LIST_CURRENT, @@ -1407,7 +1406,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M .with_check_width(theme::CHECKLIST_CHECK_WIDTH) .with_current_offset(theme::CHECKLIST_CURRENT_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) })), ), @@ -1440,20 +1439,23 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut let obj = if info_button { LayoutObj::new(Frame::left_aligned( theme::label_title(), - notification, + notification.into(), Dialog::new( paragraphs, - Button::<StrBuffer>::cancel_info_confirm( - TR::buttons__continue.try_into()?, - TR::buttons__more_info.try_into()?, + Button::cancel_info_confirm( + TR::buttons__continue.into(), + TR::buttons__more_info.into(), ), ), ))? } else { LayoutObj::new(Frame::left_aligned( theme::label_title(), - notification, - Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))), + notification.into(), + Dialog::new( + paragraphs, + Button::cancel_confirm_text(None, Some(button.into())), + ), ))? }; Ok(obj.into()) @@ -1477,7 +1479,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - title, + title.into(), Dialog::new(paragraphs, SelectWordCount::new()), ))?; Ok(obj.into()) @@ -1496,11 +1498,9 @@ extern "C" fn new_show_group_share_success( let obj = LayoutObj::new(IconDialog::new_shares( lines, - theme::button_bar( - Button::<StrBuffer>::with_text(TR::buttons__continue.try_into()?).map(|msg| { - (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) - }), - ), + theme::button_bar(Button::with_text(TR::buttons__continue.into()).map(|msg| { + (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed) + })), ))?; Ok(obj.into()) }; @@ -1519,12 +1519,11 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs: .add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after()); } - let tr_title: StrBuffer = TR::recovery__title_remaining_shares.try_into()?; let obj = LayoutObj::new(Frame::left_aligned( theme::label_title(), - tr_title, - ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG) - .with_cancel_confirm(None, Some(TR::buttons__continue.try_into()?)) + TR::recovery__title_remaining_shares.into(), + ButtonPage::new(paragraphs.into_paragraphs(), theme::BG) + .with_cancel_confirm(None, Some(TR::buttons__continue.into())) .with_confirm_style(theme::button_default()) .without_cancel(), ))?; @@ -1550,7 +1549,11 @@ 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 // convert them to StrBuffer. - let obj = LayoutObj::new(Progress::new(title, indeterminate, description.into()))?; + let obj = LayoutObj::new(Progress::new( + title.into(), + indeterminate, + description.into(), + ))?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1565,7 +1568,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 // 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 timeout = Timeout::new(time_ms); LayoutObj::new((timeout, progress.map(|_msg| None)))? @@ -1642,19 +1645,17 @@ extern "C" fn new_confirm_firmware_update( let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?; - let title_str = TR::firmware_update__title.try_into()?; + let title_str = TR::firmware_update__title.into(); 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 = - Button::with_text(TR::buttons__cancel.try_into()?).styled(theme::button_default()); - let right = - Button::with_text(TR::buttons__install.try_into()?).styled(theme::button_confirm()); + let left = Button::with_text(TR::buttons__cancel.into()).styled(theme::button_default()); + let right = Button::with_text(TR::buttons__install.into()).styled(theme::button_confirm()); let obj = LayoutObj::new( Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info( - TR::firmware_update__title_fingerprint.try_into()?, - fingerprint, + TR::firmware_update__title_fingerprint.into(), + fingerprint.into(), theme::button_moreinfo(), ), )?; @@ -2190,8 +2191,11 @@ mod tests { #[test] fn trace_example_layout() { - let buttons = - Button::cancel_confirm(Button::with_text("Left"), Button::with_text("Right"), false); + let buttons = Button::cancel_confirm( + Button::with_text("Left".into()), + Button::with_text("Right".into()), + false, + ); let ops = OpTextLayout::new(theme::TEXT_NORMAL) .text_normal("Testing text layout, with some text, and some more text. And ") diff --git a/core/embed/rust/src/ui/model_tt/screens.rs b/core/embed/rust/src/ui/model_tt/screens.rs index 381569d25f..db01e8c628 100644 --- a/core/embed/rust/src/ui/model_tt/screens.rs +++ b/core/embed/rust/src/ui/model_tt/screens.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "micropython")] -use crate::micropython::buffer::StrBuffer; use crate::ui::{ component::Component, constant::screen, @@ -10,26 +8,8 @@ use crate::ui::{ }, }; -#[cfg(not(feature = "micropython"))] -// SAFETY: Actually safe but see below -unsafe fn get_str(text: &str) -> &str { - text -} -#[cfg(feature = "micropython")] -// SAFETY: The caller is responsible for ensuring that the StrBuffer does not -// escape the lifetime of the original &str. -unsafe fn get_str(text: &str) -> StrBuffer { - unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) } -} - pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) { - // SAFETY: these will get placed into `frame` which does not outlive this - // function - let title = unsafe { get_str(title) }; - let msg = unsafe { get_str(msg) }; - 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.paint(); }