diff --git a/core/embed/rust/src/strutil.rs b/core/embed/rust/src/strutil.rs index 51c4b5a69c..0054584efb 100644 --- a/core/embed/rust/src/strutil.rs +++ b/core/embed/rust/src/strutil.rs @@ -78,7 +78,7 @@ pub enum TString<'a> { Str(&'a str), } -impl<'a> TString<'a> { +impl TString<'_> { pub fn len(&self) -> usize { self.map(|s| s.len()) } @@ -87,10 +87,10 @@ impl<'a> TString<'a> { self.len() == 0 } - pub fn map(&'a self, fun: F) -> T + pub fn map(&self, fun: F) -> T where - F: FnOnce(&'a str) -> T, - T: 'a, + F: for<'a> FnOnce(&'a str) -> T, + T: 'static, { match self { #[cfg(feature = "micropython")] @@ -147,12 +147,6 @@ impl<'a> From<&'a str> for TString<'a> { } } -impl<'a> From<&'a TString<'a>> for &'a str { - fn from(s: &'a TString<'a>) -> &'a str { - s.map(|s| s) - } -} - #[cfg(feature = "translations")] impl From for TString<'static> { fn from(tr: TR) -> Self { diff --git a/core/embed/rust/src/translations/translated_string.rs b/core/embed/rust/src/translations/translated_string.rs index 55c79b47b0..b41ef228af 100644 --- a/core/embed/rust/src/translations/translated_string.rs +++ b/core/embed/rust/src/translations/translated_string.rs @@ -10,9 +10,10 @@ impl TranslatedString { .unwrap_or(self.untranslated()) } - pub fn map_translated<'a, F, T>(self, fun: F) -> T + pub fn map_translated(self, fun: F) -> T where - F: FnOnce(&'a str) -> T, + F: for<'a> FnOnce(&'a str) -> T, + T: 'static, { // SAFETY: The bound on F _somehow_ ensures that the reference cannot escape // the closure. (I don't understand how, but it does), see soundness test below. diff --git a/core/embed/rust/src/ui/component/label.rs b/core/embed/rust/src/ui/component/label.rs index 9e90630232..6399210010 100644 --- a/core/embed/rust/src/ui/component/label.rs +++ b/core/embed/rust/src/ui/component/label.rs @@ -63,25 +63,22 @@ impl<'a> Label<'a> { pub fn max_size(&self) -> Offset { let font = self.font(); - Offset::new( - font.text_width(self.text.map(|c| c)), - 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.map(|c| c)) - .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.map(|c| c)); + 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() { @@ -98,10 +95,8 @@ impl Component for Label<'_> { fn place(&mut self, bounds: Rect) -> Rect { let height = self - .layout - .with_bounds(bounds) - .fit_text(self.text.map(|c| c)) - .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 @@ impl Component for Label<'_> { } fn paint(&mut self) { - self.layout.render_text(self.text.map(|c| c)); + self.text.map(|c| self.layout.render_text(c)); } #[cfg(feature = "ui_bounds")] @@ -130,6 +125,6 @@ impl Component for Label<'_> { impl crate::trace::Trace for Label<'_> { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Label"); - t.string("text", self.text.map(|c| c).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 29445bb27a..c2f39a0819 100644 --- a/core/embed/rust/src/ui/component/marquee.rs +++ b/core/embed/rust/src/ui/component/marquee.rs @@ -64,7 +64,7 @@ impl Marquee { } if let State::Initial = self.state { - let text_width = self.font.text_width(self.text.map(|t| t)); + let text_width = self.text.map(|t| self.font.text_width(t)); let max_offset = self.area.width() - text_width; self.min_offset = 0; @@ -120,14 +120,8 @@ impl Marquee { } pub fn paint_anim(&mut self, offset: i16) { - display::marquee( - self.area, - self.text.map(|t| t), - offset, - self.font, - self.fg, - self.bg, - ); + self.text + .map(|t| display::marquee(self.area, t, offset, self.font, self.fg, self.bg)); } } 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 67b8e3d3ed..f3f2f6b439 100644 --- a/core/embed/rust/src/ui/model_tr/component/homescreen.rs +++ b/core/embed/rust/src/ui/model_tr/component/homescreen.rs @@ -110,11 +110,11 @@ impl Homescreen { .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.map(|c| c), 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.map(|c| c)); + 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(), 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 762d81f644..5340e719d5 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 @@ -152,13 +152,15 @@ impl<'a> PinEntry<'a> { let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt { ( false, - TR::pin__title_wrong_pin.map_translated(String::from), - String::from(subprompt.map(|t| t)), + TR::pin__title_wrong_pin + .as_tstring() + .map(|t| String::from(t)), + subprompt.map(|t| String::from(t)), ) } else { ( true, - String::from(prompt.map(|t| t)), + prompt.map(|t| String::from(t)), String::from(EMPTY_PIN_STR), ) }; @@ -201,7 +203,7 @@ impl<'a> PinEntry<'a> { let pin_line_text = if self.is_empty() && !self.subprompt.is_empty() { // Showing the subprompt in NORMAL font used_font = Font::NORMAL; - String::from(self.subprompt.map(|t| t)) + self.subprompt.map(|t| String::from(t)) } else if self.is_empty() { String::from(EMPTY_PIN_STR) } else if self.show_real_pin { @@ -231,7 +233,8 @@ impl<'a> PinEntry<'a> { /// 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(String::from(self.prompt.map(|t| t))); + self.prompt + .map(|t| header_line.update_text(String::from(t))); header_line.request_complete_repaint(ctx); }); } 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 ef25c8bbbb..1af329d820 100644 --- a/core/embed/rust/src/ui/model_tr/component/progress.rs +++ b/core/embed/rust/src/ui/model_tr/component/progress.rs @@ -86,7 +86,7 @@ impl Component for Progress { 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().map(|c| c).is_empty() { + if !self_title.inner().text().map(|c| c.is_empty()) { let (title, rest) = Self::AREA.split_top(self_title.inner().max_size().y); (title, rest, LOADER_Y_OFFSET_TITLE) } else { 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 74a22b7fbd..f75d488df9 100644 --- a/core/embed/rust/src/ui/model_tr/component/title.rs +++ b/core/embed/rust/src/ui/model_tr/component/title.rs @@ -34,14 +34,14 @@ impl Title { self } - pub fn get_text(&self) -> &str { - self.title.map(|s| s) + pub fn get_text(&self) -> TString { + self.title } 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 = theme::FONT_HEADER.text_width(new_text.map(|s| s)); + 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(); @@ -54,26 +54,18 @@ impl Title { 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.map(|s| s), - 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: &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.map(|s| s), - theme::FONT_HEADER, - theme::FG, - theme::BG, - ); + title.map(|s| { + display::text_center(title_baseline, s, theme::FONT_HEADER, theme::FG, theme::BG) + }); } } @@ -83,7 +75,7 @@ impl Component for Title { fn place(&mut self, bounds: Rect) -> Rect { self.area = bounds; self.marquee.place(bounds); - let width = theme::FONT_HEADER.text_width(self.title.map(|s| s)); + let width = self.title.map(|s| theme::FONT_HEADER.text_width(s)); self.needs_marquee = width > self.area.width(); bounds } @@ -115,6 +107,6 @@ impl Component for Title { impl crate::trace::Trace for Title { fn trace(&self, t: &mut dyn crate::trace::Tracer) { t.component("Title"); - t.string("text", self.title.map(|t| t).into()); + t.string("text", self.title); } } 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 3dab0430ed..fdf11ca15f 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -191,18 +191,20 @@ impl Button { match &self.content { ButtonContent::Empty => {} ButtonContent::Text(text) => { - let width = style.font.text_width(text.map(|c| c)); + 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.into(), - 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( 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 7d7d539790..0b5712f067 100644 --- a/core/embed/rust/src/ui/model_tt/component/fido.rs +++ b/core/embed/rust/src/ui/model_tt/component/fido.rs @@ -178,9 +178,7 @@ 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.) - if !current_account.map(|c| c).is_empty() - && current_account.map(|c| c) != self.app_name.text().map(|c| c) - { + if current_account.map(|c| !c.is_empty() && self.app_name.text().map(|t| t != c)) { self.account_name.set_text(current_account); self.account_name.paint(); } 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 fa29c9d9db..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 @@ -44,7 +44,7 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif" /// supplied. pub fn get_fido_icon_data(icon_name: Option>) -> &'static [u8] { if let Some(icon_name) = icon_name { - match icon_name.map(|c| c) { + icon_name.map(|c| match c { "apple" => ICON_APPLE, "aws" => ICON_AWS, "binance" => ICON_BINANCE, @@ -76,7 +76,7 @@ pub fn get_fido_icon_data(icon_name: Option>) -> &'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 2767fe6fbc..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 @@ -26,12 +26,12 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif" /// supplied. pub fn get_fido_icon_data(icon_name: Option>) -> &'static [u8] { if let Some(icon_name) = icon_name { - match icon_name.map(|c| c) { + 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/keyboard/passphrase.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/passphrase.rs index d198e34127..0844fe0321 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 @@ -273,7 +273,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.map(|c| c)); + 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 7c5870f69f..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 @@ -233,7 +233,9 @@ impl Component for PinKeyboard<'_> { 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.into())); + text.map(|text| { + self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text)); + }); self.pin_modified(ctx); return None; } @@ -462,8 +464,11 @@ impl crate::trace::Trace for PinKeyboard<'_> { 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.into())); + 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/page.rs b/core/embed/rust/src/ui/model_tt/component/page.rs index 4cb6364416..153604b478 100644 --- a/core/embed/rust/src/ui/model_tt/component/page.rs +++ b/core/embed/rust/src/ui/model_tt/component/page.rs @@ -90,11 +90,11 @@ where right: Option>, ) -> Self { let cancel = match left { - Some(verb) => match verb.map(|s| s) { + 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 {