1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-25 16:08:32 +00:00

WIP - add DELETE option in recovery words

This commit is contained in:
grdddj 2023-01-10 14:31:47 +01:00
parent cd0c4d1cbd
commit 96d0a7b2e7
6 changed files with 63 additions and 43 deletions

View File

@ -497,11 +497,11 @@ impl ButtonLayout {
/// Default button layout for all three buttons - icons. /// Default button layout for all three buttons - icons.
pub fn default_three_icons() -> Self { pub fn default_three_icons() -> Self {
Self::arrow_armed_icon("SELECT".into()) Self::arrow_armed_arrow("SELECT".into())
} }
/// Special middle text for default icon layout. /// Special middle text for default icon layout.
pub fn arrow_armed_icon(text: StrBuffer) -> Self { pub fn arrow_armed_arrow(text: StrBuffer) -> Self {
Self::new( Self::new(
Some(ButtonDetails::left_arrow_icon()), Some(ButtonDetails::left_arrow_icon()),
Some(ButtonDetails::armed_text(text)), Some(ButtonDetails::armed_text(text)),
@ -518,6 +518,15 @@ impl ButtonLayout {
) )
} }
/// Middle armed text and next right arrow.
pub fn none_armed_arrow(text: StrBuffer) -> Self {
Self::new(
None,
Some(ButtonDetails::armed_text(text)),
Some(ButtonDetails::right_arrow_icon()),
)
}
/// Left cancel, armed text and right text. /// Left cancel, armed text and right text.
pub fn cancel_armed_text(middle: StrBuffer, right: StrBuffer) -> Self { pub fn cancel_armed_text(middle: StrBuffer, right: StrBuffer) -> Self {
Self::new( Self::new(

View File

@ -156,12 +156,12 @@ where
&mut self, &mut self,
ctx: &mut EventCtx, ctx: &mut EventCtx,
new_choices: F, new_choices: F,
reset_page_counter: bool, new_page_counter: Option<u8>,
is_carousel: bool, is_carousel: bool,
) { ) {
self.choices = new_choices; self.choices = new_choices;
if reset_page_counter { if let Some(new_counter) = new_page_counter {
self.page_counter = 0; self.page_counter = new_counter;
} }
self.is_carousel = is_carousel; self.is_carousel = is_carousel;
self.update(ctx); self.update(ctx);

View File

@ -135,7 +135,7 @@ impl ChoiceFactoryPassphrase {
/// return back /// return back
fn get_character_item(&self, choice_index: u8) -> ChoiceItem { fn get_character_item(&self, choice_index: u8) -> ChoiceItem {
if is_menu_choice(&self.current_category, choice_index) { if is_menu_choice(&self.current_category, choice_index) {
ChoiceItem::new("MENU", ButtonLayout::arrow_armed_icon("RETURN".into())) ChoiceItem::new("MENU", ButtonLayout::arrow_armed_arrow("RETURN".into()))
} else { } else {
let ch = get_char(&self.current_category, choice_index); let ch = get_char(&self.current_category, choice_index);
ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons()) ChoiceItem::new(char_to_string::<1>(ch), ButtonLayout::default_three_icons())
@ -211,7 +211,7 @@ impl PassphraseEntry {
/// Displaying the MENU /// Displaying the MENU
fn show_menu_page(&mut self, ctx: &mut EventCtx) { fn show_menu_page(&mut self, ctx: &mut EventCtx) {
let menu_choices = ChoiceFactoryPassphrase::new(ChoiceCategory::Menu); let menu_choices = ChoiceFactoryPassphrase::new(ChoiceCategory::Menu);
self.choice_page.reset(ctx, menu_choices, true, false); self.choice_page.reset(ctx, menu_choices, Some(0), false);
// Going back to the last MENU position before showing the MENU // Going back to the last MENU position before showing the MENU
self.choice_page.set_page_counter(ctx, self.menu_position); self.choice_page.set_page_counter(ctx, self.menu_position);
} }
@ -219,7 +219,7 @@ impl PassphraseEntry {
/// Displaying the character category /// Displaying the character category
fn show_category_page(&mut self, ctx: &mut EventCtx) { fn show_category_page(&mut self, ctx: &mut EventCtx) {
let category_choices = ChoiceFactoryPassphrase::new(self.current_category.clone()); let category_choices = ChoiceFactoryPassphrase::new(self.current_category.clone());
self.choice_page.reset(ctx, category_choices, true, true); self.choice_page.reset(ctx, category_choices, Some(0), true);
} }
pub fn passphrase(&self) -> &str { pub fn passphrase(&self) -> &str {

View File

@ -24,6 +24,12 @@ const MAX_LETTERS_LENGTH: usize = 26;
/// 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;
/// Where will be the DELETE option - at the first position
const DELETE_INDEX: u8 = 0;
/// Which index will be used at the beginning.
/// (Accounts for DELETE to be at index 0)
const INITIAL_PAGE_COUNTER: u8 = DELETE_INDEX + 1;
const PROMPT: &str = "_"; const PROMPT: &str = "_";
/// Type of the wordlist, deciding the list of words to be used /// Type of the wordlist, deciding the list of words to be used
@ -61,13 +67,15 @@ impl ChoiceFactory for ChoiceFactoryWordlist {
fn get(&self, choice_index: u8) -> ChoiceItem { fn get(&self, choice_index: u8) -> ChoiceItem {
// Letters have a carousel, words do not // Letters have a carousel, words do not
// Putting DELETE as the first option in both cases
// (is a requirement for WORDS, doing it for LETTERS as well to unite it)
match self { match self {
Self::Letters(letter_choices) => { Self::Letters(letter_choices) => {
if choice_index >= letter_choices.len() as u8 { if choice_index == DELETE_INDEX {
ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_icon("CONFIRM".into())) ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_arrow("CONFIRM".into()))
.with_icon(Icon::new(theme::ICON_DELETE)) .with_icon(Icon::new(theme::ICON_DELETE))
} else { } else {
let letter = letter_choices[choice_index as usize]; let letter = letter_choices[choice_index as usize - 1];
ChoiceItem::new( ChoiceItem::new(
char_to_string::<1>(letter), char_to_string::<1>(letter),
ButtonLayout::default_three_icons(), ButtonLayout::default_three_icons(),
@ -75,17 +83,14 @@ impl ChoiceFactory for ChoiceFactoryWordlist {
} }
} }
Self::Words(word_choices) => { Self::Words(word_choices) => {
if choice_index >= word_choices.len() as u8 { if choice_index == DELETE_INDEX {
let mut item = ChoiceItem::new("DELETE", ButtonLayout::none_armed_arrow("CONFIRM".into()))
ChoiceItem::new("DELETE", ButtonLayout::arrow_armed_icon("CONFIRM".into())) .with_icon(Icon::new(theme::ICON_DELETE))
.with_icon(Icon::new(theme::ICON_DELETE));
item.set_right_btn(None);
item
} else { } else {
let word = word_choices[choice_index as usize]; let word = word_choices[choice_index as usize - 1];
let mut item = ChoiceItem::new(word, ButtonLayout::default_three_icons()); let mut item = ChoiceItem::new(word, ButtonLayout::default_three_icons());
if choice_index == 0 { if choice_index == self.count() - 1 {
item.set_left_btn(None); item.set_right_btn(None);
} }
item item
} }
@ -113,9 +118,11 @@ impl WordlistEntry {
let choices = ChoiceFactoryWordlist::letters(letter_choices.clone()); let choices = ChoiceFactoryWordlist::letters(letter_choices.clone());
Self { Self {
// Starting at second page because of DELETE option
choice_page: ChoicePage::new(choices) choice_page: ChoicePage::new(choices)
.with_incomplete(true) .with_incomplete(true)
.with_carousel(true), .with_carousel(true)
.with_initial_page_counter(INITIAL_PAGE_COUNTER),
chosen_letters: Child::new(ChangingTextLine::center_mono(String::from(PROMPT))), chosen_letters: Child::new(ChangingTextLine::center_mono(String::from(PROMPT))),
letter_choices, letter_choices,
textbox: TextBox::empty(), textbox: TextBox::empty(),
@ -160,8 +167,12 @@ impl WordlistEntry {
let new_choices = self.get_current_choices(); let new_choices = self.get_current_choices();
// Not using carousel in case of words, as that looks weird in case // Not using carousel in case of words, as that looks weird in case
// there is only one word to choose from. // there is only one word to choose from.
self.choice_page self.choice_page.reset(
.reset(ctx, new_choices, true, !self.offer_words); ctx,
new_choices,
Some(INITIAL_PAGE_COUNTER),
!self.offer_words,
);
ctx.request_paint(); ctx.request_paint();
} }
@ -211,21 +222,21 @@ impl Component for WordlistEntry {
// Clicked SELECT. // Clicked SELECT.
// When we already offer words, return the word at the given index. // When we already offer words, return the word at the given index.
// Otherwise, resetting the choice page with up-to-date choices. // Otherwise, resetting the choice page with up-to-date choices.
if page_counter as usize == self.delete_index() { if page_counter == DELETE_INDEX {
// Clicked DELETE. Deleting last letter, updating wordlist and updating choices // Clicked DELETE. Deleting last letter, updating wordlist and updating choices
self.delete_last_letter(ctx); self.delete_last_letter(ctx);
self.reset_wordlist(); self.reset_wordlist();
self.update(ctx); self.update(ctx);
} else if self.offer_words {
let word = self
.words_list
.get(page_counter as usize)
.unwrap_or_default();
return Some(WordlistEntryMsg::ResultWord(String::from(word)));
} else { } else {
let new_letter = self.letter_choices[page_counter as usize]; let index = page_counter as usize - 1;
self.append_letter(ctx, new_letter); if self.offer_words {
self.update(ctx); let word = self.words_list.get(index).unwrap_or_default();
return Some(WordlistEntryMsg::ResultWord(String::from(word)));
} else {
let new_letter = self.letter_choices[index];
self.append_letter(ctx, new_letter);
self.update(ctx);
}
} }
} }
@ -252,16 +263,16 @@ impl crate::trace::Trace for WordlistEntry {
ButtonPos::Left => ButtonAction::PrevPage.string(), ButtonPos::Left => ButtonAction::PrevPage.string(),
ButtonPos::Right => ButtonAction::NextPage.string(), ButtonPos::Right => ButtonAction::NextPage.string(),
ButtonPos::Middle => { ButtonPos::Middle => {
let current_index = self.choice_page.page_index() as usize; let current_index = self.choice_page.page_index();
let choice: String<10> = if current_index == self.delete_index() { let choice: String<10> = if current_index == DELETE_INDEX {
String::from("DELETE") String::from("DELETE")
} else if self.offer_words {
self.words_list
.get(current_index)
.unwrap_or_default()
.into()
} else { } else {
util::char_to_string(self.letter_choices[current_index]) let index = current_index as usize - 1;
if self.offer_words {
self.words_list.get(index).unwrap_or_default().into()
} else {
util::char_to_string(self.letter_choices[index])
}
}; };
ButtonAction::select_item(choice) ButtonAction::select_item(choice)
} }

View File

@ -509,7 +509,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
} else { } else {
// Page in the middle // Page in the middle
( (
ButtonLayout::arrow_armed_icon("SELECT".into()), ButtonLayout::arrow_armed_arrow("SELECT".into()),
ButtonActions::prev_confirm_next(), ButtonActions::prev_confirm_next(),
) )
}; };

View File

@ -1309,7 +1309,7 @@ async def pin_mismatch(
ctx, ctx,
br_type, br_type,
"PIN MISMATCH", "PIN MISMATCH",
"The PINs you entered do not match.\nPlease try again.", description="The PINs you entered do not match.\nPlease try again.",
verb="TRY AGAIN", verb="TRY AGAIN",
verb_cancel=None, verb_cancel=None,
br_code=br_code, br_code=br_code,