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

pull/3649/head
tychovrahe 2 months ago
parent 7defafc9d7
commit a045dce2ae

@ -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<F, T>(&'a self, fun: F) -> T
pub fn map<F, T>(&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<TR> for TString<'static> {
fn from(tr: TR) -> Self {

@ -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<F, T>(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.

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

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

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

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

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

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

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

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

@ -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<TString<'static>>) -> &'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<TString<'static>>) -> &'static [u8]
"stripe" => ICON_STRIPE,
"tutanota" => ICON_TUTANOTA,
_ => ICON_WEBAUTHN,
}
})
} else {
ICON_WEBAUTHN
}

@ -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<TString<'static>>) -> &'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
}

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

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

@ -90,11 +90,11 @@ where
right: Option<TString<'static>>,
) -> 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 {

Loading…
Cancel
Save