1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-01 19:38:33 +00:00

WIP - bip39 design improvements

This commit is contained in:
grdddj 2022-11-14 09:49:57 +01:00
parent f1536c602b
commit 3434838bbe
8 changed files with 89 additions and 42 deletions

View File

@ -515,7 +515,13 @@ impl<T: Clone + AsRef<str>> ButtonDetails<T> {
self
}
/// Duration of the hold-to-confirm.
/// Default duration of the hold-to-confirm.
pub fn with_default_duration(mut self) -> Self {
self.duration = Some(Duration::from_millis(1000));
self
}
/// Specific duration of the hold-to-confirm.
pub fn with_duration(mut self, duration: Duration) -> Self {
self.duration = Some(duration);
self

View File

@ -83,6 +83,21 @@ pub fn paint_header_left<T: AsRef<str>>(title: T, area: Rect) -> i16 {
text_heigth
}
/// Display title/header centered at the top of the given area.
/// Returning the painted height of the whole header.
pub fn paint_header_centered<T: AsRef<str>>(title: T, area: Rect) -> i16 {
let text_heigth = theme::FONT_HEADER.text_height();
let title_baseline = area.top_center() + Offset::y(text_heigth);
display::text_center(
title_baseline,
title.as_ref(),
theme::FONT_HEADER,
theme::FG,
theme::BG,
);
text_heigth
}
/// Draws icon and text on the same line - icon on the left.
pub fn icon_with_text<T: AsRef<str>>(baseline: Point, icon: Icon, text: T, font: Font) {
icon.draw_bottom_left(baseline, theme::FG, theme::BG);

View File

@ -5,9 +5,12 @@ use crate::ui::{
};
/// Component for holding another component and displaying a title.
/// Also is allocating space for a scrollbar.
pub struct Frame<T, U> {
area: Rect,
title: U,
title_centered: bool,
account_for_scrollbar: bool,
content: Child<T>,
}
@ -20,6 +23,8 @@ where
Self {
title,
area: Rect::zero(),
title_centered: false,
account_for_scrollbar: true,
content: Child::new(content),
}
}
@ -27,6 +32,20 @@ where
pub fn inner(&self) -> &T {
self.content.inner()
}
/// Aligning the title to the center, instead of the left.
/// Also disabling scrollbar in this case, as they are not compatible.
pub fn with_title_center(mut self, title_centered: bool) -> Self {
self.title_centered = title_centered;
self.account_for_scrollbar = false;
self
}
/// Allocating space for scrollbar in the top right. True by default.
pub fn with_scrollbar(mut self, account_for_scrollbar: bool) -> Self {
self.account_for_scrollbar = account_for_scrollbar;
self
}
}
impl<T, U> Component for Frame<T, U>
@ -43,10 +62,16 @@ where
bounds.split_top(theme::FONT_HEADER.line_height());
let content_area = content_area.inset(Insets::top(TITLE_SPACE));
// Title area is different based on scrollbar.
let title_area = if self.account_for_scrollbar {
let (title_area, scrollbar_area) =
title_and_scrollbar_area.split_right(ScrollBar::MAX_WIDTH);
// Sending the scrollbar area to the child component.
self.content.set_scrollbar_area(scrollbar_area);
title_area
} else {
title_and_scrollbar_area
};
self.area = title_area;
self.content.place(content_area);
@ -58,7 +83,11 @@ where
}
fn paint(&mut self) {
if self.title_centered {
common::paint_header_centered(&self.title, self.area);
} else {
common::paint_header_left(&self.title, self.area);
}
self.content.paint();
}
}

View File

@ -1,12 +1,9 @@
use crate::{
time::Duration,
ui::{
use crate::ui::{
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
display::Icon,
geometry::Rect,
model_tr::theme,
util::char_to_string,
},
};
use super::{
@ -31,7 +28,6 @@ enum ChoiceCategory {
}
const MAX_PASSPHRASE_LENGTH: usize = 50;
const HOLD_DURATION: Duration = Duration::from_secs(1);
const DIGITS: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const LOWERCASE_LETTERS: [char; 26] = [
@ -115,14 +111,10 @@ impl ChoiceFactoryPassphrase {
// Including accept button on the left and cancel on the very right.
// TODO: could have some icons instead of the shortcut text
if choice_index == 0 {
menu_item.set_left_btn(Some(
ButtonDetails::text("ACC").with_duration(HOLD_DURATION),
));
menu_item.set_left_btn(Some(ButtonDetails::text("ACC").with_default_duration()));
}
if choice_index == MENU.len() as u8 - 1 {
menu_item.set_right_btn(Some(
ButtonDetails::text("CAN").with_duration(HOLD_DURATION),
));
menu_item.set_right_btn(Some(ButtonDetails::text("CAN").with_default_duration()));
}
// Including icons for some items.

View File

@ -175,7 +175,7 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
// Optional HoldToConfirm
if hold {
// TODO: clients might want to set the duration
confirm_btn = confirm_btn.map(|btn| btn.with_duration(Duration::from_secs(2)));
confirm_btn = confirm_btn.map(|btn| btn.with_default_duration());
}
let content = ButtonPage::new_str_buf(
@ -255,8 +255,7 @@ extern "C" fn confirm_properties(n_args: usize, args: *const Obj, kwargs: *mut M
let mut content = ButtonPage::new_str(paragraphs.into_paragraphs(), theme::BG);
if hold {
let confirm_btn =
Some(ButtonDetails::text("CONFIRM").with_duration(Duration::from_secs(1)));
let confirm_btn = Some(ButtonDetails::text("CONFIRM").with_default_duration());
content = content.with_confirm_btn(confirm_btn);
}
let obj = LayoutObj::new(Frame::new(title, content))?;
@ -298,10 +297,7 @@ extern "C" fn confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map)
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()),
None,
Some(
ButtonDetails::text("HOLD TO CONFIRM")
.with_duration(Duration::from_secs(2)),
),
Some(ButtonDetails::text("HOLD TO CONFIRM").with_default_duration()),
);
let btn_actions = ButtonActions::cancel_confirm();
Page::<20>::new(btn_layout, btn_actions, Font::MONO)
@ -342,7 +338,7 @@ extern "C" fn confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -
let btn_layout = ButtonLayout::new(
Some(ButtonDetails::cancel_icon()),
None,
Some(ButtonDetails::text("HOLD TO SEND").with_duration(Duration::from_secs(2))),
Some(ButtonDetails::text("HOLD TO SEND").with_default_duration()),
);
let btn_actions = ButtonActions::cancel_confirm();
@ -523,8 +519,7 @@ extern "C" fn show_share_words(n_args: usize, args: *const Obj, kwargs: *mut Map
let share_words_obj: Obj = kwargs.get(Qstr::MP_QSTR_share_words)?;
let share_words: Vec<StrBuffer, 24> = iter_into_vec(share_words_obj)?;
let confirm_btn =
Some(ButtonDetails::text("HOLD TO CONFIRM").with_duration(Duration::from_secs(1)));
let confirm_btn = Some(ButtonDetails::text("HOLD TO CONFIRM").with_default_duration());
let obj = LayoutObj::new(
ButtonPage::new_str(ShareWords::new(share_words), theme::BG)
@ -564,7 +559,7 @@ extern "C" fn request_word_bip39(n_args: usize, args: *const Obj, kwargs: *mut 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, Bip39Entry::new()))?;
let obj = LayoutObj::new(Frame::new(prompt, Bip39Entry::new()).with_title_center(true))?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }

View File

@ -66,7 +66,7 @@ async def _continue_recovery_process(ctx: GenericContext) -> Success:
# If we are starting recovery, ask for word count first...
# _request_word_count
await layout.homescreen_dialog(
ctx, "Select", "Select the number of words in your recovery seed"
ctx, "Select", "Select the number of words in your recovery seed."
)
# ask for the number of words
word_count = await layout.request_word_count(ctx, dry_run)

View File

@ -419,11 +419,12 @@ async def get_bool(
ctx: wire.GenericContext,
br_type: str,
title: str,
data: str,
data: str | None = None,
description: str | None = None,
verb: str | None = "CONFIRM",
verb_cancel: str | None = "",
hold: bool = False,
reverse: bool = False,
br_code: ButtonRequestType = ButtonRequestType.Other,
) -> bool:
result = await interact(
@ -436,7 +437,7 @@ async def get_bool(
verb=verb,
verb_cancel=verb_cancel,
hold=hold,
reverse=False,
reverse=reverse,
)
),
br_type,
@ -526,7 +527,7 @@ async def confirm_reset_device(
if show_tutorial:
await tutorial(ctx)
to_show = "By continuing you agree to our terms and conditions.\n\nMore info at trezor.io/tos."
to_show = "By continuing you agree to our terms and conditions.\nMore info at trezor.io/tos."
if not recovery:
to_show += "\nUse you backup to recover your wallet."

View File

@ -73,11 +73,20 @@ async def continue_recovery(
info_func: Callable | None,
dry_run: bool,
) -> bool:
# NOTE: no need to implement `info_func`, as it is used only in
# Shamir backup, which is not implemented for TR.
description = text
if subtext:
description += f"\n\n{subtext}"
return await get_bool(
ctx=ctx,
title="START RECOVERY",
data=f"{text}\n\n{subtext or ''}",
verb="START",
br_type="recovery",
ctx,
"recovery",
"START RECOVERY",
None,
description,
verb="HOLD TO BEGIN",
hold=True,
reverse=True,
br_code=ButtonRequestType.RecoveryHomepage,
)