mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-04 11:51:50 +00:00
refactor(core/rust): drop ui::macros
removing inttostr!, build_string!, and relocating include_res!
This commit is contained in:
parent
8134490e2e
commit
da37bce59d
@ -14,7 +14,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::layout::{LayoutFit, TextLayout, TextStyle};
|
use super::layout::{LayoutFit, TextLayout, TextStyle};
|
||||||
use heapless::String;
|
|
||||||
|
|
||||||
/// Used as an upper bound of number of different styles we may render on single
|
/// Used as an upper bound of number of different styles we may render on single
|
||||||
/// page.
|
/// page.
|
||||||
@ -656,7 +655,7 @@ where
|
|||||||
color: Color,
|
color: Color,
|
||||||
target: &mut impl Renderer<'s>,
|
target: &mut impl Renderer<'s>,
|
||||||
) {
|
) {
|
||||||
let numeral = build_string!(10, inttostr!(n as u8 + 1), ".");
|
let numeral = uformat!("{}.", n + 1);
|
||||||
shape::Text::new(base_point, numeral.as_str())
|
shape::Text::new(base_point, numeral.as_str())
|
||||||
.with_font(Font::NORMAL)
|
.with_font(Font::NORMAL)
|
||||||
.with_fg(color)
|
.with_fg(color)
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
#[allow(unused_macros)] // T1 doesn't use icons (yet)
|
|
||||||
macro_rules! include_res {
|
|
||||||
($filename:expr) => {
|
|
||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/ui/", $filename))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Concatenates arbitrary amount of slices into a String.
|
|
||||||
macro_rules! build_string {
|
|
||||||
($max:expr, $($string:expr),+) => {
|
|
||||||
{
|
|
||||||
let mut new_string = String::<$max>::new();
|
|
||||||
$(unwrap!(new_string.push_str($string));)+
|
|
||||||
new_string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms integer into string slice. For example for printing.
|
|
||||||
#[allow(unused_macros)] // not used in TT UI
|
|
||||||
macro_rules! inttostr {
|
|
||||||
($int:expr) => {{
|
|
||||||
unwrap!(heapless::String::<10>::try_from($int)).as_str()
|
|
||||||
}};
|
|
||||||
}
|
|
@ -1,6 +1,3 @@
|
|||||||
#[macro_use]
|
|
||||||
pub mod macros;
|
|
||||||
|
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
pub mod backlight;
|
pub mod backlight;
|
||||||
@ -14,7 +11,6 @@ pub mod flow;
|
|||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod lerp;
|
pub mod lerp;
|
||||||
pub mod shape;
|
pub mod shape;
|
||||||
#[macro_use]
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use crate::strutil::TString;
|
use crate::strutil::TString;
|
||||||
|
use crate::ui::util::include_res;
|
||||||
|
|
||||||
|
|
||||||
const ICON_APPLE: &[u8] = include_res!("model_mercury/res/fido/icon_apple.toif");
|
const ICON_APPLE: &[u8] = include_res!("model_mercury/res/fido/icon_apple.toif");
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use crate::strutil::TString;
|
use crate::strutil::TString;
|
||||||
|
use crate::ui::util::include_res;
|
||||||
|
|
||||||
<%
|
<%
|
||||||
icons: list[tuple[str, str]] = []
|
icons: list[tuple[str, str]] = []
|
||||||
|
@ -9,12 +9,11 @@ use crate::{
|
|||||||
event::SwipeEvent,
|
event::SwipeEvent,
|
||||||
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||||
model_mercury::component::Footer,
|
model_mercury::component::Footer,
|
||||||
shape,
|
shape::{self, Renderer},
|
||||||
shape::Renderer,
|
|
||||||
util,
|
util,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use heapless::{String, Vec};
|
use heapless::Vec;
|
||||||
|
|
||||||
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
|
||||||
const ANIMATION_DURATION_MS: Duration = Duration::from_millis(166);
|
const ANIMATION_DURATION_MS: Duration = Duration::from_millis(166);
|
||||||
@ -158,7 +157,7 @@ impl<'a> Component for ShareWords<'a> {
|
|||||||
.text_font
|
.text_font
|
||||||
.visible_text_height("1"),
|
.visible_text_height("1"),
|
||||||
);
|
);
|
||||||
let ordinal = build_string!(3, inttostr!(ordinal_val), ".");
|
let ordinal = uformat!("{}.", ordinal_val);
|
||||||
shape::Text::new(ordinal_pos, &ordinal)
|
shape::Text::new(ordinal_pos, &ordinal)
|
||||||
.with_font(theme::TEXT_SUB_GREY_LIGHT.text_font)
|
.with_font(theme::TEXT_SUB_GREY_LIGHT.text_font)
|
||||||
.with_fg(theme::GREY)
|
.with_fg(theme::GREY)
|
||||||
@ -204,8 +203,7 @@ impl<'a> crate::trace::Trace for ShareWords<'a> {
|
|||||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
t.component("ShareWords");
|
t.component("ShareWords");
|
||||||
let word = &self.share_words[self.page_index as usize];
|
let word = &self.share_words[self.page_index as usize];
|
||||||
let content =
|
let content = word.map(|w| uformat!("{}. {}\n", self.page_index + 1, w));
|
||||||
word.map(|w| build_string!(50, inttostr!(self.page_index as u8 + 1), ". ", w, "\n"));
|
|
||||||
t.string("screen_content", content.as_str().into());
|
t.string("screen_content", content.as_str().into());
|
||||||
t.int("page_count", self.share_words.len() as i64)
|
t.int("page_count", self.share_words.len() as i64)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::ui::{
|
|||||||
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
|
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
|
||||||
constant::{HEIGHT, WIDTH},
|
constant::{HEIGHT, WIDTH},
|
||||||
display::{Color, Font},
|
display::{Color, Font},
|
||||||
geometry::{Offset, Point, Rect},
|
geometry::{Offset, Point, Rect}, util::include_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::{
|
use super::super::{
|
||||||
|
@ -9,8 +9,8 @@ use crate::{
|
|||||||
text::{layout::Chunks, LineBreaking, PageBreaking, TextStyle},
|
text::{layout::Chunks, LineBreaking, PageBreaking, TextStyle},
|
||||||
FixedHeightBar,
|
FixedHeightBar,
|
||||||
},
|
},
|
||||||
display::{Color, Font, Icon},
|
display::{Color, Font},
|
||||||
geometry::Insets,
|
geometry::Insets, util::include_icon,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
component::{Component, Event, EventCtx, Never, Pad},
|
strutil::ShortString,
|
||||||
display::Font,
|
ui::{
|
||||||
geometry::{Alignment, Point, Rect},
|
component::{Component, Event, EventCtx, Never, Pad},
|
||||||
shape,
|
display::Font,
|
||||||
shape::Renderer,
|
geometry::{Alignment, Point, Rect},
|
||||||
util::long_line_content_with_ellipsis,
|
shape::{self, Renderer},
|
||||||
|
util::long_line_content_with_ellipsis,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{common, theme};
|
use super::{common, theme};
|
||||||
@ -12,9 +14,9 @@ use super::{common, theme};
|
|||||||
/// Component that allows for "allocating" a standalone line of text anywhere
|
/// Component that allows for "allocating" a standalone line of text anywhere
|
||||||
/// on the screen and updating it arbitrarily - without affecting the rest
|
/// on the screen and updating it arbitrarily - without affecting the rest
|
||||||
/// and without being affected by other components.
|
/// and without being affected by other components.
|
||||||
pub struct ChangingTextLine<T> {
|
pub struct ChangingTextLine {
|
||||||
pad: Pad,
|
pad: Pad,
|
||||||
text: T,
|
text: ShortString,
|
||||||
font: Font,
|
font: Font,
|
||||||
/// Whether to show the text. Can be disabled.
|
/// Whether to show the text. Can be disabled.
|
||||||
show_content: bool,
|
show_content: bool,
|
||||||
@ -25,11 +27,10 @@ pub struct ChangingTextLine<T> {
|
|||||||
text_at_the_top: bool,
|
text_at_the_top: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ChangingTextLine<T>
|
impl ChangingTextLine {
|
||||||
where
|
pub fn new(text: &str, font: Font, alignment: Alignment, max_len: usize) -> Self {
|
||||||
T: AsRef<str>,
|
let text = unwrap!(ShortString::try_from(text));
|
||||||
{
|
debug_assert!(text.capacity() >= max_len);
|
||||||
pub fn new(text: T, font: Font, alignment: Alignment) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
pad: Pad::with_background(theme::BG),
|
pad: Pad::with_background(theme::BG),
|
||||||
text,
|
text,
|
||||||
@ -41,12 +42,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center_mono(text: T) -> Self {
|
pub fn center_mono(text: &str, max_len: usize) -> Self {
|
||||||
Self::new(text, Font::MONO, Alignment::Center)
|
Self::new(text, Font::MONO, Alignment::Center, max_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center_bold(text: T) -> Self {
|
pub fn center_bold(text: &str, max_len: usize) -> Self {
|
||||||
Self::new(text, Font::BOLD_UPPER, Alignment::Center)
|
Self::new(text, Font::BOLD_UPPER, Alignment::Center, max_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Not showing ellipsis at the beginning of longer texts.
|
/// Not showing ellipsis at the beginning of longer texts.
|
||||||
@ -62,13 +63,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the text to be displayed in the line.
|
/// Update the text to be displayed in the line.
|
||||||
pub fn update_text(&mut self, text: T) {
|
pub fn update_text(&mut self, text: &str) {
|
||||||
self.text = text;
|
self.text.clear();
|
||||||
|
unwrap!(self.text.push_str(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current text.
|
/// Get current text.
|
||||||
pub fn get_text(&self) -> &T {
|
pub fn get_text(&self) -> &str {
|
||||||
&self.text
|
self.text.as_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changing the current font
|
/// Changing the current font
|
||||||
@ -155,21 +157,14 @@ where
|
|||||||
// Creating the notion of motion by shifting the text left and right with
|
// Creating the notion of motion by shifting the text left and right with
|
||||||
// each new text character.
|
// each new text character.
|
||||||
// (So that it is apparent for the user that the text is changing.)
|
// (So that it is apparent for the user that the text is changing.)
|
||||||
let x_offset = if self.text.as_ref().len() % 2 == 0 {
|
let x_offset = if self.text.len() % 2 == 0 { 0 } else { 2 };
|
||||||
0
|
|
||||||
} else {
|
|
||||||
2
|
|
||||||
};
|
|
||||||
|
|
||||||
let baseline = Point::new(self.pad.area.x0 + x_offset, self.y_baseline());
|
let baseline = Point::new(self.pad.area.x0 + x_offset, self.y_baseline());
|
||||||
common::display_left(baseline, &text_to_display, self.font);
|
common::display_left(baseline, &text_to_display, self.font);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Component for ChangingTextLine<T>
|
impl Component for ChangingTextLine {
|
||||||
where
|
|
||||||
T: AsRef<str>,
|
|
||||||
{
|
|
||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::TString,
|
strutil::{ShortString, TString},
|
||||||
translations::TR,
|
translations::TR,
|
||||||
trezorhal::random,
|
trezorhal::random,
|
||||||
ui::{
|
ui::{
|
||||||
@ -270,7 +270,7 @@ impl ChoiceFactory for ChoiceFactoryPassphrase {
|
|||||||
/// Component for entering a passphrase.
|
/// Component for entering a passphrase.
|
||||||
pub struct PassphraseEntry {
|
pub struct PassphraseEntry {
|
||||||
choice_page: ChoicePage<ChoiceFactoryPassphrase, PassphraseAction>,
|
choice_page: ChoicePage<ChoiceFactoryPassphrase, PassphraseAction>,
|
||||||
passphrase_dots: Child<ChangingTextLine<String<MAX_PASSPHRASE_LENGTH>>>,
|
passphrase_dots: Child<ChangingTextLine>,
|
||||||
show_plain_passphrase: bool,
|
show_plain_passphrase: bool,
|
||||||
show_last_digit: bool,
|
show_last_digit: bool,
|
||||||
textbox: TextBox<MAX_PASSPHRASE_LENGTH>,
|
textbox: TextBox<MAX_PASSPHRASE_LENGTH>,
|
||||||
@ -283,7 +283,7 @@ impl PassphraseEntry {
|
|||||||
choice_page: ChoicePage::new(ChoiceFactoryPassphrase::new(ChoiceCategory::Menu, true))
|
choice_page: ChoicePage::new(ChoiceFactoryPassphrase::new(ChoiceCategory::Menu, true))
|
||||||
.with_carousel(true)
|
.with_carousel(true)
|
||||||
.with_initial_page_counter(random_menu_position()),
|
.with_initial_page_counter(random_menu_position()),
|
||||||
passphrase_dots: Child::new(ChangingTextLine::center_mono(String::new())),
|
passphrase_dots: Child::new(ChangingTextLine::center_mono("", MAX_PASSPHRASE_LENGTH)),
|
||||||
show_plain_passphrase: false,
|
show_plain_passphrase: false,
|
||||||
show_last_digit: false,
|
show_last_digit: false,
|
||||||
textbox: TextBox::empty(),
|
textbox: TextBox::empty(),
|
||||||
@ -298,7 +298,7 @@ impl PassphraseEntry {
|
|||||||
unwrap!(String::try_from(""))
|
unwrap!(String::try_from(""))
|
||||||
} else {
|
} else {
|
||||||
// Showing asterisks and possibly the last digit.
|
// Showing asterisks and possibly the last digit.
|
||||||
let mut dots: String<MAX_PASSPHRASE_LENGTH> = String::new();
|
let mut dots: ShortString = String::new();
|
||||||
for _ in 0..self.textbox.len() - 1 {
|
for _ in 0..self.textbox.len() - 1 {
|
||||||
unwrap!(dots.push('*'));
|
unwrap!(dots.push('*'));
|
||||||
}
|
}
|
||||||
@ -311,7 +311,7 @@ impl PassphraseEntry {
|
|||||||
dots
|
dots
|
||||||
};
|
};
|
||||||
self.passphrase_dots.mutate(ctx, |ctx, passphrase_dots| {
|
self.passphrase_dots.mutate(ctx, |ctx, passphrase_dots| {
|
||||||
passphrase_dots.update_text(text_to_show);
|
passphrase_dots.update_text(&text_to_show);
|
||||||
passphrase_dots.request_complete_repaint(ctx);
|
passphrase_dots.request_complete_repaint(ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::TString,
|
strutil::{ShortString, TString},
|
||||||
translations::TR,
|
translations::TR,
|
||||||
trezorhal::random,
|
trezorhal::random,
|
||||||
ui::{
|
ui::{
|
||||||
@ -132,8 +132,8 @@ impl ChoiceFactory for ChoiceFactoryPIN {
|
|||||||
/// Component for entering a PIN.
|
/// Component for entering a PIN.
|
||||||
pub struct PinEntry<'a> {
|
pub struct PinEntry<'a> {
|
||||||
choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>,
|
choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>,
|
||||||
header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
header_line: Child<ChangingTextLine>,
|
||||||
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
pin_line: Child<ChangingTextLine>,
|
||||||
prompt: TString<'a>,
|
prompt: TString<'a>,
|
||||||
subprompt: TString<'a>,
|
subprompt: TString<'a>,
|
||||||
/// Whether we already show the "real" prompt (not the warning).
|
/// Whether we already show the "real" prompt (not the warning).
|
||||||
@ -151,20 +151,13 @@ impl<'a> PinEntry<'a> {
|
|||||||
// any button click.)
|
// any button click.)
|
||||||
let show_subprompt = !subprompt.is_empty();
|
let show_subprompt = !subprompt.is_empty();
|
||||||
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
|
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
|
||||||
(
|
(false, TR::pin__title_wrong_pin.into(), subprompt)
|
||||||
false,
|
|
||||||
TR::pin__title_wrong_pin.map_translated(|t| unwrap!(String::try_from(t))),
|
|
||||||
subprompt.map(|s| unwrap!(String::try_from(s))),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(
|
(true, prompt, EMPTY_PIN_STR.into())
|
||||||
true,
|
|
||||||
prompt.map(|s| unwrap!(String::try_from(s))),
|
|
||||||
unwrap!(String::try_from(EMPTY_PIN_STR)),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut pin_line = ChangingTextLine::center_bold(pin_line_content).without_ellipsis();
|
let mut pin_line = pin_line_content
|
||||||
|
.map(|s| ChangingTextLine::center_bold(s, MAX_PIN_LENGTH).without_ellipsis());
|
||||||
if show_subprompt {
|
if show_subprompt {
|
||||||
pin_line.update_font(Font::NORMAL);
|
pin_line.update_font(Font::NORMAL);
|
||||||
}
|
}
|
||||||
@ -175,7 +168,8 @@ impl<'a> PinEntry<'a> {
|
|||||||
.with_initial_page_counter(get_random_digit_position())
|
.with_initial_page_counter(get_random_digit_position())
|
||||||
.with_carousel(true),
|
.with_carousel(true),
|
||||||
header_line: Child::new(
|
header_line: Child::new(
|
||||||
ChangingTextLine::center_bold(header_line_content)
|
header_line_content
|
||||||
|
.map(|s| ChangingTextLine::center_bold(s, MAX_PIN_LENGTH))
|
||||||
.without_ellipsis()
|
.without_ellipsis()
|
||||||
.with_text_at_the_top(),
|
.with_text_at_the_top(),
|
||||||
),
|
),
|
||||||
@ -209,7 +203,7 @@ impl<'a> PinEntry<'a> {
|
|||||||
unwrap!(String::try_from(self.pin()))
|
unwrap!(String::try_from(self.pin()))
|
||||||
} else {
|
} else {
|
||||||
// Showing asterisks and possibly the last digit.
|
// Showing asterisks and possibly the last digit.
|
||||||
let mut dots: String<MAX_PIN_LENGTH> = String::new();
|
let mut dots = ShortString::new();
|
||||||
for _ in 0..self.textbox.len() - 1 {
|
for _ in 0..self.textbox.len() - 1 {
|
||||||
unwrap!(dots.push('*'));
|
unwrap!(dots.push('*'));
|
||||||
}
|
}
|
||||||
@ -224,7 +218,7 @@ impl<'a> PinEntry<'a> {
|
|||||||
|
|
||||||
self.pin_line.mutate(ctx, |ctx, pin_line| {
|
self.pin_line.mutate(ctx, |ctx, pin_line| {
|
||||||
pin_line.update_font(used_font);
|
pin_line.update_font(used_font);
|
||||||
pin_line.update_text(pin_line_text);
|
pin_line.update_text(&pin_line_text);
|
||||||
pin_line.request_complete_repaint(ctx);
|
pin_line.request_complete_repaint(ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -232,7 +226,7 @@ impl<'a> PinEntry<'a> {
|
|||||||
/// Showing the real prompt instead of WRONG PIN
|
/// Showing the real prompt instead of WRONG PIN
|
||||||
fn show_prompt(&mut self, ctx: &mut EventCtx) {
|
fn show_prompt(&mut self, ctx: &mut EventCtx) {
|
||||||
self.header_line.mutate(ctx, |ctx, header_line| {
|
self.header_line.mutate(ctx, |ctx, header_line| {
|
||||||
header_line.update_text(self.prompt.map(|s| unwrap!(String::try_from(s))));
|
self.prompt.map(|s| header_line.update_text(s));
|
||||||
header_line.request_complete_repaint(ctx);
|
header_line.request_complete_repaint(ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ enum WordlistAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MAX_WORD_LENGTH: usize = 10;
|
const MAX_WORD_LENGTH: usize = 10;
|
||||||
|
const LINE_CAPACITY: usize = MAX_WORD_LENGTH + 1;
|
||||||
|
|
||||||
/// Offer words when there will be fewer of them than this
|
/// Offer words when there will be fewer of them than this
|
||||||
const OFFER_WORDS_THRESHOLD: usize = 10;
|
const OFFER_WORDS_THRESHOLD: usize = 10;
|
||||||
@ -156,7 +157,7 @@ impl ChoiceFactory for ChoiceFactoryWordlist {
|
|||||||
/// Component for entering a mnemonic from a wordlist - BIP39 or SLIP39.
|
/// Component for entering a mnemonic from a wordlist - BIP39 or SLIP39.
|
||||||
pub struct WordlistEntry {
|
pub struct WordlistEntry {
|
||||||
choice_page: ChoicePage<ChoiceFactoryWordlist, WordlistAction>,
|
choice_page: ChoicePage<ChoiceFactoryWordlist, WordlistAction>,
|
||||||
chosen_letters: Child<ChangingTextLine<String<{ MAX_WORD_LENGTH + 1 }>>>,
|
chosen_letters: Child<ChangingTextLine>,
|
||||||
textbox: TextBox<MAX_WORD_LENGTH>,
|
textbox: TextBox<MAX_WORD_LENGTH>,
|
||||||
offer_words: bool,
|
offer_words: bool,
|
||||||
wordlist_type: WordlistType,
|
wordlist_type: WordlistType,
|
||||||
@ -174,9 +175,7 @@ impl WordlistEntry {
|
|||||||
.with_incomplete(true)
|
.with_incomplete(true)
|
||||||
.with_carousel(true)
|
.with_carousel(true)
|
||||||
.with_initial_page_counter(get_random_position(choices_count)),
|
.with_initial_page_counter(get_random_position(choices_count)),
|
||||||
chosen_letters: Child::new(ChangingTextLine::center_mono(unwrap!(String::try_from(
|
chosen_letters: Child::new(ChangingTextLine::center_mono(PROMPT, LINE_CAPACITY)),
|
||||||
PROMPT
|
|
||||||
)))),
|
|
||||||
textbox: TextBox::empty(),
|
textbox: TextBox::empty(),
|
||||||
offer_words: false,
|
offer_words: false,
|
||||||
wordlist_type,
|
wordlist_type,
|
||||||
@ -196,9 +195,7 @@ impl WordlistEntry {
|
|||||||
choice_page: ChoicePage::new(choices)
|
choice_page: ChoicePage::new(choices)
|
||||||
.with_incomplete(true)
|
.with_incomplete(true)
|
||||||
.with_initial_page_counter(1),
|
.with_initial_page_counter(1),
|
||||||
chosen_letters: Child::new(ChangingTextLine::center_mono(unwrap!(String::try_from(
|
chosen_letters: Child::new(ChangingTextLine::center_mono(word, LINE_CAPACITY)),
|
||||||
word
|
|
||||||
)))),
|
|
||||||
textbox: TextBox::new(unwrap!(String::try_from(word))),
|
textbox: TextBox::new(unwrap!(String::try_from(word))),
|
||||||
offer_words: false,
|
offer_words: false,
|
||||||
wordlist_type,
|
wordlist_type,
|
||||||
@ -263,9 +260,9 @@ impl WordlistEntry {
|
|||||||
|
|
||||||
/// Reflects currently chosen letters in the textbox.
|
/// Reflects currently chosen letters in the textbox.
|
||||||
fn update_chosen_letters(&mut self, ctx: &mut EventCtx) {
|
fn update_chosen_letters(&mut self, ctx: &mut EventCtx) {
|
||||||
let text = build_string!({ MAX_WORD_LENGTH + 1 }, self.textbox.content(), PROMPT);
|
let text = uformat!("{}{}", self.textbox.content(), PROMPT);
|
||||||
self.chosen_letters.mutate(ctx, |ctx, chosen_letters| {
|
self.chosen_letters.mutate(ctx, |ctx, chosen_letters| {
|
||||||
chosen_letters.update_text(text);
|
chosen_letters.update_text(&text);
|
||||||
chosen_letters.request_complete_repaint(ctx);
|
chosen_letters.request_complete_repaint(ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::StringType,
|
strutil::{ShortString, StringType, TString},
|
||||||
translations::TR,
|
translations::TR,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
@ -8,12 +8,12 @@ use crate::{
|
|||||||
},
|
},
|
||||||
display::Font,
|
display::Font,
|
||||||
geometry::{Alignment, Offset, Rect},
|
geometry::{Alignment, Offset, Rect},
|
||||||
shape,
|
shape::{self, Renderer},
|
||||||
shape::Renderer,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use heapless::{String, Vec};
|
use heapless::Vec;
|
||||||
|
use ufmt::uwrite;
|
||||||
|
|
||||||
use super::{common::display_left, scrollbar::SCROLLBAR_SPACE, theme, ScrollBar};
|
use super::{common::display_left, scrollbar::SCROLLBAR_SPACE, theme, ScrollBar};
|
||||||
|
|
||||||
@ -72,15 +72,10 @@ where
|
|||||||
word_screens + 1
|
word_screens + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_final_text(&self) -> String<50> {
|
fn get_final_text(&self) -> ShortString {
|
||||||
TR::share_words__wrote_down_all.map_translated(|wrote_down_all| {
|
TR::share_words__wrote_down_all.map_translated(|wrote_down_all| {
|
||||||
TR::share_words__words_in_order.map_translated(|in_order| {
|
TR::share_words__words_in_order.map_translated(|in_order| {
|
||||||
build_string!(
|
uformat!("{}{}{}", wrote_down_all, self.share_words.len(), in_order)
|
||||||
50,
|
|
||||||
wrote_down_all,
|
|
||||||
inttostr!(self.share_words.len() as u8),
|
|
||||||
in_order
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -124,7 +119,7 @@ where
|
|||||||
}
|
}
|
||||||
let word = &self.share_words[index];
|
let word = &self.share_words[index];
|
||||||
let baseline = self.area.top_left() + Offset::y(y_offset);
|
let baseline = self.area.top_left() + Offset::y(y_offset);
|
||||||
let ordinal = build_string!(5, inttostr!(index as u8 + 1), ".");
|
let ordinal = uformat!("{}.", index + 1);
|
||||||
display_left(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal, NUMBER_FONT);
|
display_left(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal, NUMBER_FONT);
|
||||||
display_left(baseline + Offset::x(WORD_X_OFFSET), word, WORD_FONT);
|
display_left(baseline + Offset::x(WORD_X_OFFSET), word, WORD_FONT);
|
||||||
}
|
}
|
||||||
@ -142,7 +137,7 @@ where
|
|||||||
}
|
}
|
||||||
let word = &self.share_words[index];
|
let word = &self.share_words[index];
|
||||||
let baseline = self.area.top_left() + Offset::y(y_offset);
|
let baseline = self.area.top_left() + Offset::y(y_offset);
|
||||||
let ordinal = build_string!(5, inttostr!(index as u8 + 1), ".");
|
let ordinal = uformat!("{}.", index + 1);
|
||||||
|
|
||||||
shape::Text::new(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal)
|
shape::Text::new(baseline + Offset::x(NUMBER_X_OFFSET), &ordinal)
|
||||||
.with_font(NUMBER_FONT)
|
.with_font(NUMBER_FONT)
|
||||||
@ -230,16 +225,14 @@ where
|
|||||||
let content = if self.is_final_page() {
|
let content = if self.is_final_page() {
|
||||||
self.get_final_text()
|
self.get_final_text()
|
||||||
} else {
|
} else {
|
||||||
let mut content = String::<50>::new();
|
let mut content = ShortString::new();
|
||||||
for i in 0..WORDS_PER_PAGE {
|
for i in 0..WORDS_PER_PAGE {
|
||||||
let index = self.word_index() + i;
|
let index = self.word_index() + i;
|
||||||
if index >= self.share_words.len() {
|
if index >= self.share_words.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let word = &self.share_words[index];
|
let word: TString = self.share_words[index].clone().into();
|
||||||
let current_line =
|
unwrap!(uwrite!(content, "{}. {}\n", index + 1, word));
|
||||||
build_string!(50, inttostr!(index as u8 + 1), ". ", word.as_ref(), "\n");
|
|
||||||
unwrap!(content.push_str(¤t_line));
|
|
||||||
}
|
}
|
||||||
content
|
content
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::text::TextStyle,
|
component::text::TextStyle,
|
||||||
display::{toif::Icon, Color, Font},
|
display::{Color, Font},
|
||||||
|
util::include_icon,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use super::super::theme::{BLACK, WHITE};
|
pub use super::super::theme::{BLACK, WHITE};
|
||||||
|
@ -3,8 +3,9 @@ use crate::ui::{
|
|||||||
text::{layout::Chunks, TextStyle},
|
text::{layout::Chunks, TextStyle},
|
||||||
LineBreaking, PageBreaking,
|
LineBreaking, PageBreaking,
|
||||||
},
|
},
|
||||||
display::{toif::Icon, Color, Font},
|
display::{Color, Font},
|
||||||
geometry::Offset,
|
geometry::Offset,
|
||||||
|
util::include_icon,
|
||||||
};
|
};
|
||||||
|
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use crate::strutil::TString;
|
use crate::strutil::TString;
|
||||||
|
use crate::ui::util::include_res;
|
||||||
|
|
||||||
|
|
||||||
const ICON_APPLE: &[u8] = include_res!("model_tt/res/fido/icon_apple.toif");
|
const ICON_APPLE: &[u8] = include_res!("model_tt/res/fido/icon_apple.toif");
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use crate::strutil::TString;
|
use crate::strutil::TString;
|
||||||
|
use crate::ui::util::include_res;
|
||||||
|
|
||||||
<%
|
<%
|
||||||
icons: list[tuple[str, str]] = []
|
icons: list[tuple[str, str]] = []
|
||||||
|
@ -6,7 +6,7 @@ use crate::ui::{
|
|||||||
model_tt::{
|
model_tt::{
|
||||||
component::{ButtonStyle, ButtonStyleSheet, ResultStyle},
|
component::{ButtonStyle, ButtonStyleSheet, ResultStyle},
|
||||||
theme::{BLACK, FG, GREY_DARK, GREY_LIGHT, WHITE},
|
theme::{BLACK, FG, GREY_DARK, GREY_LIGHT, WHITE},
|
||||||
},
|
}, util::include_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const BLD_BG: Color = Color::rgb(0x00, 0x1E, 0xAD);
|
pub const BLD_BG: Color = Color::rgb(0x00, 0x1E, 0xAD);
|
||||||
|
@ -8,8 +8,9 @@ use crate::{
|
|||||||
text::{layout::Chunks, LineBreaking, PageBreaking, TextStyle},
|
text::{layout::Chunks, LineBreaking, PageBreaking, TextStyle},
|
||||||
FixedHeightBar,
|
FixedHeightBar,
|
||||||
},
|
},
|
||||||
display::{Color, Font, Icon},
|
display::{Color, Font},
|
||||||
geometry::{Insets, Offset},
|
geometry::{Insets, Offset},
|
||||||
|
util::{include_icon, include_res},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -155,20 +155,29 @@ pub fn long_line_content_with_ellipsis(
|
|||||||
let remaining_available_width = available_width - ellipsis_width;
|
let remaining_available_width = available_width - ellipsis_width;
|
||||||
let chars_from_right = text_font.longest_suffix(remaining_available_width, text);
|
let chars_from_right = text_font.longest_suffix(remaining_available_width, text);
|
||||||
|
|
||||||
build_string!(50, ellipsis, &text[text.len() - chars_from_right..])
|
let mut s = ShortString::new();
|
||||||
|
unwrap!(s.push_str(ellipsis));
|
||||||
|
unwrap!(s.push_str(&text[text.len() - chars_from_right..]));
|
||||||
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
/// Create the `Icon` constant with given name and path.
|
/// Create the `Icon` constant with given name and path.
|
||||||
/// Possibly users can supply `true` as a third argument and this
|
/// Possibly users can supply `true` as a third argument and this
|
||||||
/// will signify that the icon has empty right column.
|
/// will signify that the icon has empty right column.
|
||||||
macro_rules! include_icon {
|
macro_rules! include_icon {
|
||||||
($name:ident, $path:expr, empty_right_col = $empty:expr) => {
|
($name:ident, $path:expr, empty_right_col = $empty:expr) => {
|
||||||
pub const $name: Icon = if $empty {
|
pub const $name: $crate::ui::display::toif::Icon = if $empty {
|
||||||
Icon::debug_named(include_res!($path), stringify!($name)).with_empty_right_column()
|
$crate::ui::display::toif::Icon::debug_named(
|
||||||
|
$crate::ui::util::include_res!($path),
|
||||||
|
stringify!($name),
|
||||||
|
)
|
||||||
|
.with_empty_right_column()
|
||||||
} else {
|
} else {
|
||||||
Icon::debug_named(include_res!($path), stringify!($name))
|
$crate::ui::display::toif::Icon::debug_named(
|
||||||
|
$crate::ui::util::include_res!($path),
|
||||||
|
stringify!($name),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
// No empty right column by default.
|
// No empty right column by default.
|
||||||
@ -176,6 +185,14 @@ macro_rules! include_icon {
|
|||||||
include_icon!($name, $path, empty_right_col = false);
|
include_icon!($name, $path, empty_right_col = false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub(crate) use include_icon;
|
||||||
|
|
||||||
|
macro_rules! include_res {
|
||||||
|
($filename:expr) => {
|
||||||
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/ui/", $filename))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use include_res;
|
||||||
|
|
||||||
pub const SLIDE_DURATION_MS: Duration = Duration::from_millis(333);
|
pub const SLIDE_DURATION_MS: Duration = Duration::from_millis(333);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user