From e16a37b0cf1d6397af6bd7cc1011ca3d648d1197 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Fri, 10 Mar 2023 16:39:29 +0100 Subject: [PATCH] fix(core/ui): style update: mnemonic keyboards [no changelog] --- .../rust/src/ui/model_tt/component/frame.rs | 29 +++------ .../ui/model_tt/component/keyboard/bip39.rs | 9 +-- .../model_tt/component/keyboard/mnemonic.rs | 8 ++- .../model_tt/component/keyboard/passphrase.rs | 22 +++---- .../src/ui/model_tt/component/keyboard/pin.rs | 8 +-- .../ui/model_tt/component/keyboard/slip39.rs | 6 +- .../model_tt/component/keyboard/word_count.rs | 4 +- .../src/ui/model_tt/component/number_input.rs | 5 +- core/embed/rust/src/ui/model_tt/layout.rs | 41 ++++++------ core/embed/rust/src/ui/model_tt/theme.rs | 64 +++++++++++++++++++ core/src/trezor/ui/layouts/tt_v2/recovery.py | 2 +- tests/click_tests/recovery.py | 2 +- .../test_shamir_persistence.py | 4 +- 13 files changed, 127 insertions(+), 77 deletions(-) diff --git a/core/embed/rust/src/ui/model_tt/component/frame.rs b/core/embed/rust/src/ui/model_tt/component/frame.rs index e7a81f37f1..b0864ddf78 100644 --- a/core/embed/rust/src/ui/model_tt/component/frame.rs +++ b/core/embed/rust/src/ui/model_tt/component/frame.rs @@ -3,9 +3,8 @@ use crate::ui::{ component::{ base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx, }, - display::{self, toif::Icon, Color}, + display::{self, Color, Font}, geometry::{Alignment, Insets, Offset, Rect}, - util::icon_text_center, }; pub struct Frame { @@ -116,7 +115,6 @@ where pub struct NotificationFrame { area: Rect, - icon: Icon, title: U, content: Child, } @@ -128,13 +126,10 @@ where { const HEIGHT: i16 = 36; const COLOR: Color = theme::YELLOW; - const TEXT_OFFSET: Offset = Offset::new(1, -2); - const ICON_SPACE: i16 = 8; const BORDER: i16 = 6; - pub fn new(icon: Icon, title: U, content: T) -> Self { + pub fn new(title: U, content: T) -> Self { Self { - icon, title, area: Rect::zero(), content: Child::new(content), @@ -145,22 +140,18 @@ where self.content.inner() } - pub fn paint_notification(area: Rect, icon: Icon, title: &str, color: Color) { + pub fn paint_notification(area: Rect, title: &str, color: Color) { let (area, _) = area .inset(Insets::uniform(Self::BORDER)) .split_top(Self::HEIGHT); - let style = TextStyle { - background_color: color, - ..theme::TEXT_BOLD - }; + let font = Font::BOLD; display::rect_fill_rounded(area, color, theme::BG, 2); - icon_text_center( - area.center(), - icon, - Self::ICON_SPACE, + display::text_center( + area.center() + Offset::y((font.text_max_height() - font.text_baseline()) / 2), title, - style, - Self::TEXT_OFFSET, + Font::BOLD, + theme::FG, + color, ); } } @@ -184,7 +175,7 @@ where } fn paint(&mut self) { - Self::paint_notification(self.area, self.icon, self.title.as_ref(), Self::COLOR); + Self::paint_notification(self.area, self.title.as_ref(), Self::COLOR); self.content.paint(); } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs index 6ea210ee13..31601df79e 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/bip39.rs @@ -215,20 +215,21 @@ impl Bip39Input { { // Confirm button. self.button.enable(ctx); - self.button.set_stylesheet(ctx, theme::button_confirm()); + self.button.set_stylesheet(ctx, theme::button_pin_confirm()); self.button - .set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM))); + .set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_LIST_CHECK))); } else { // Auto-complete button. self.button.enable(ctx); - self.button.set_stylesheet(ctx, theme::button_default()); + self.button + .set_stylesheet(ctx, theme::button_pin_autocomplete()); self.button .set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CLICK))); } } else { // Disabled button. self.button.disable(ctx); - self.button.set_stylesheet(ctx, theme::button_default()); + self.button.set_stylesheet(ctx, theme::button_pin()); self.button.set_content(ctx, ButtonContent::Text("")); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs index 19c76e8100..cf5aeb9120 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/mnemonic.rs @@ -43,7 +43,7 @@ where Icon::new(theme::ICON_BACK), Offset::new(30, 17), ) - .styled(theme::button_clear()) + .styled(theme::button_reset()) .with_long_press(theme::ERASE_HOLD_DURATION), )), input: Child::new(Maybe::hidden(theme::BG, input)), @@ -96,8 +96,10 @@ where type Msg = MnemonicKeyboardMsg; fn place(&mut self, bounds: Rect) -> Rect { - let grid = - Grid::new(bounds.inset(theme::borders()), 4, 3).with_spacing(theme::KEYBOARD_SPACING); + let (_, bounds) = bounds + .inset(theme::borders()) + .split_bottom(4 * theme::MNEMONIC_BUTTON_HEIGHT + 3 * theme::KEYBOARD_SPACING); + let grid = Grid::new(bounds, 4, 3).with_spacing(theme::KEYBOARD_SPACING); let back_area = grid.row_col(0, 0); let input_area = grid.row_col(0, 1).union(grid.row_col(0, 3)); 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 22516324ca..7e0995e106 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 @@ -2,13 +2,10 @@ use crate::ui::{ component::{base::ComponentExt, Child, Component, Event, EventCtx, Never}, display, display::toif::Icon, - geometry::{Grid, Insets, Offset, Rect}, + geometry::{Grid, Offset, Rect}, model_tt::component::{ button::{Button, ButtonContent, ButtonMsg}, - keyboard::common::{ - paint_pending_marker, MultiTapKeyboard, TextBox, HEADER_HEIGHT, HEADER_PADDING_BOTTOM, - HEADER_PADDING_SIDE, - }, + keyboard::common::{paint_pending_marker, MultiTapKeyboard, TextBox}, swipe::{Swipe, SwipeDirection}, theme, ScrollBar, }, @@ -41,6 +38,7 @@ const KEYBOARD: [[&str; KEY_COUNT]; PAGE_COUNT] = [ ]; const MAX_LENGTH: usize = 50; +const INPUT_AREA_HEIGHT: i16 = ScrollBar::DOT_SIZE + 9; impl PassphraseKeyboard { pub fn new() -> Self { @@ -59,8 +57,9 @@ impl PassphraseKeyboard { .initially_enabled(false) .with_long_press(theme::ERASE_HOLD_DURATION) .into_child(), - keys: KEYBOARD[STARTING_PAGE] - .map(|text| Child::new(Button::new(Self::key_content(text)))), + keys: KEYBOARD[STARTING_PAGE].map(|text| { + Child::new(Button::new(Self::key_content(text)).styled(theme::button_pin())) + }), scrollbar: ScrollBar::horizontal(), fade: false, } @@ -152,14 +151,13 @@ impl Component for PassphraseKeyboard { fn place(&mut self, bounds: Rect) -> Rect { let bounds = bounds.inset(theme::borders()); - let (input_area, key_grid_area) = bounds.split_top(HEADER_HEIGHT + HEADER_PADDING_BOTTOM); + let (input_area, key_grid_area) = + bounds.split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::BUTTON_SPACING); - let (input_area, scroll_area) = - input_area.split_bottom(ScrollBar::DOT_SIZE + theme::KEYBOARD_SPACING); + let (input_area, scroll_area) = input_area.split_bottom(INPUT_AREA_HEIGHT); let (scroll_area, _) = scroll_area.split_top(ScrollBar::DOT_SIZE); - let input_area = input_area.inset(Insets::sides(HEADER_PADDING_SIDE)); - let key_grid = Grid::new(key_grid_area, 4, 3).with_spacing(theme::KEYBOARD_SPACING); + let key_grid = Grid::new(key_grid_area, 4, 3).with_spacing(theme::BUTTON_SPACING); let confirm_btn_area = key_grid.cell(11); let back_btn_area = key_grid.cell(9); 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 8b3d70a0fc..2301e2d7ec 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 @@ -59,8 +59,8 @@ where T: AsRef, { // Label position fine-tuning. - const MAJOR_OFF: Offset = Offset::y(-2); - const MINOR_OFF: Offset = Offset::y(-1); + const MAJOR_OFF: Offset = Offset::y(11); + const MINOR_OFF: Offset = Offset::y(11); pub fn new( major_prompt: T, @@ -165,14 +165,14 @@ where // Prompts and PIN dots display. let (header, keypad) = bounds .inset(borders_no_top) - .split_top(theme::borders().top + HEADER_HEIGHT + HEADER_PADDING_BOTTOM); + .split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::BUTTON_SPACING); let prompt = header.inset(HEADER_PADDING); // the inset -3 is a workaround for long text in "re-enter wipe code" let major_area = prompt.translate(Self::MAJOR_OFF).inset(Insets::right(-3)); let minor_area = prompt.translate(Self::MINOR_OFF); // Control buttons. - let grid = Grid::new(keypad, 4, 3).with_spacing(theme::KEYBOARD_SPACING); + let grid = Grid::new(keypad, 4, 3).with_spacing(theme::BUTTON_SPACING); // Prompts and PIN dots display. self.textbox_pad.place(header); diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs index 034f7af600..3cd3aa6ba5 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/slip39.rs @@ -224,13 +224,13 @@ impl Slip39Input { if self.final_word.is_some() { // Confirm button. self.button.enable(ctx); - self.button.set_stylesheet(ctx, theme::button_confirm()); + self.button.set_stylesheet(ctx, theme::button_pin_confirm()); self.button - .set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM))); + .set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_LIST_CHECK))); } else { // Disabled button. self.button.disable(ctx); - self.button.set_stylesheet(ctx, theme::button_default()); + self.button.set_stylesheet(ctx, theme::button_pin()); self.button.set_content(ctx, ButtonContent::Text("")); } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs index 5d580c7acf..3562419937 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/word_count.rs @@ -22,7 +22,7 @@ pub enum SelectWordCountMsg { impl SelectWordCount { pub fn new() -> Self { SelectWordCount { - button: LABELS.map(Button::with_text), + button: LABELS.map(|t| Button::with_text(t).styled(theme::button_pin())), } } } @@ -31,7 +31,7 @@ impl Component for SelectWordCount { type Msg = SelectWordCountMsg; fn place(&mut self, bounds: Rect) -> Rect { - let (_, bounds) = bounds.split_bottom(theme::button_rows(2)); + let (_, bounds) = bounds.split_bottom(2 * theme::BUTTON_HEIGHT + theme::BUTTON_SPACING); let grid = Grid::new(bounds, 2, 6).with_spacing(theme::BUTTON_SPACING); for (btn, (x, y)) in self.button.iter_mut().zip(CELLS) { btn.place(grid.cells(GridCellSpan { diff --git a/core/embed/rust/src/ui/model_tt/component/number_input.rs b/core/embed/rust/src/ui/model_tt/component/number_input.rs index f4a15cc68b..7637e2e8b0 100644 --- a/core/embed/rust/src/ui/model_tt/component/number_input.rs +++ b/core/embed/rust/src/ui/model_tt/component/number_input.rs @@ -87,13 +87,12 @@ where theme::CONTENT_BORDER, )); - let grid = Grid::new(button_area, 1, 3).with_spacing(theme::KEYBOARD_SPACING); + let grid = Grid::new(button_area, 1, 2).with_spacing(theme::KEYBOARD_SPACING); self.input.place(input_area); self.paragraphs.place(content_area); self.paragraphs_pad.place(content_area); self.info_button.place(grid.row_col(0, 0)); - self.confirm_button - .place(grid.row_col(0, 1).union(grid.row_col(0, 2))); + self.confirm_button.place(grid.row_col(0, 1)); bounds } diff --git a/core/embed/rust/src/ui/model_tt/layout.rs b/core/embed/rust/src/ui/model_tt/layout.rs index 7752de179d..c47c1c314f 100644 --- a/core/embed/rust/src/ui/model_tt/layout.rs +++ b/core/embed/rust/src/ui/model_tt/layout.rs @@ -588,15 +588,16 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let button: StrBuffer = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; - let paragraphs = Paragraphs::new(Paragraph::new( - &theme::TEXT_NORMAL, - StrBuffer::from("By continuing you agree\nto Trezor Company's\nterms and conditions."), - )); - let url = FormattedText::new( - theme::TEXT_NORMAL, - theme::FORMATTED, - "More info at {demibold}trezor.io/tos", - ); + let paragraphs = Paragraphs::new([ + Paragraph::new( + &theme::TEXT_NORMAL, + StrBuffer::from( + "By continuing you agree\nto Trezor Company's\nterms and conditions.\r", + ), + ), + Paragraph::new(&theme::TEXT_NORMAL, StrBuffer::from("More info at")), + Paragraph::new(&theme::TEXT_DEMIBOLD, StrBuffer::from("trezor.io/tos")), + ]); let buttons = Button::cancel_confirm( Button::with_icon(Icon::new(theme::ICON_CANCEL)), Button::with_text(button).styled(theme::button_confirm()), @@ -606,15 +607,7 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs: Frame::left_aligned( theme::label_title(), title, - Dialog::new( - ( - GridPlaced::new(paragraphs) - .with_grid(3, 1) - .with_from_to((0, 0), (1, 0)), - GridPlaced::new(url).with_grid(3, 1).with_row_col(2, 0), - ), - buttons, - ), + Dialog::new(paragraphs, buttons), ) .with_border(theme::borders()), )?; @@ -1299,7 +1292,7 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false).unwrap(); let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_BOLD, title).centered(), + Paragraph::new(&theme::TEXT_DEMIBOLD, title).centered(), Paragraph::new(&theme::TEXT_NORMAL_OFF_WHITE, description).centered(), ]) .with_spacing(theme::RECOVERY_SPACING); @@ -1312,7 +1305,6 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut let obj = if info_button { LayoutObj::new(NotificationFrame::new( - Icon::new(theme::ICON_WARN), notification, Dialog::new( paragraphs, @@ -1321,7 +1313,6 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut ))? } else { LayoutObj::new(NotificationFrame::new( - Icon::new(theme::ICON_WARN), notification, Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))), ))? @@ -1341,11 +1332,15 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu let title = if dry_run { "SEED CHECK" } else { - "RECOVERY MODE" + "WALLET RECOVERY" }; let paragraphs = Paragraphs::new( - Paragraph::new(&theme::TEXT_BOLD, StrBuffer::from("Number of words?")).centered(), + Paragraph::new( + &theme::TEXT_DEMIBOLD, + StrBuffer::from("Select number of words in your recovery seed."), + ) + .centered(), ); let obj = LayoutObj::new( diff --git a/core/embed/rust/src/ui/model_tt/theme.rs b/core/embed/rust/src/ui/model_tt/theme.rs index 669f319ad2..7a2dfd5acc 100644 --- a/core/embed/rust/src/ui/model_tt/theme.rs +++ b/core/embed/rust/src/ui/model_tt/theme.rs @@ -384,6 +384,70 @@ pub const fn button_pin() -> ButtonStyleSheet { } } +pub const fn button_pin_confirm() -> ButtonStyleSheet { + ButtonStyleSheet { + normal: &ButtonStyle { + font: Font::MONO, + text_color: FG, + button_color: GREEN, + background_color: BG, + border_color: BG, + border_radius: RADIUS, + border_width: 0, + }, + active: &ButtonStyle { + font: Font::MONO, + text_color: FG, + button_color: GREY_DARK, + background_color: BG, + border_color: FG, + border_radius: RADIUS, + border_width: 0, + }, + disabled: &ButtonStyle { + font: Font::MONO, + text_color: GREY_LIGHT, + button_color: GREEN, + background_color: BG, + border_color: BG, + border_radius: RADIUS, + border_width: 0, + }, + } +} + +pub const fn button_pin_autocomplete() -> ButtonStyleSheet { + ButtonStyleSheet { + normal: &ButtonStyle { + font: Font::MONO, + text_color: FG, + button_color: BG, + background_color: BG, + border_color: BG, + border_radius: RADIUS, + border_width: 0, + }, + active: &ButtonStyle { + font: Font::MONO, + text_color: FG, + button_color: GREY_DARK, + background_color: BG, + border_color: FG, + border_radius: RADIUS, + border_width: 0, + }, + disabled: &ButtonStyle { + font: Font::MONO, + text_color: GREY_LIGHT, + button_color: BG, + background_color: BG, + border_color: BG, + border_radius: RADIUS, + border_width: 0, + }, + } +} + pub const fn button_counter() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { diff --git a/core/src/trezor/ui/layouts/tt_v2/recovery.py b/core/src/trezor/ui/layouts/tt_v2/recovery.py index 9766c5dc8c..bcd59c55e4 100644 --- a/core/src/trezor/ui/layouts/tt_v2/recovery.py +++ b/core/src/trezor/ui/layouts/tt_v2/recovery.py @@ -42,7 +42,7 @@ async def request_word_count(ctx: GenericContext, dry_run: bool) -> int: async def request_word( ctx: GenericContext, word_index: int, word_count: int, is_slip39: bool ) -> str: - prompt = f"Type word {word_index + 1} of {word_count}:" + prompt = f"Type word {word_index + 1} of {word_count}" if is_slip39: keyboard = RustLayout(trezorui2.request_slip39(prompt=prompt)) else: diff --git a/tests/click_tests/recovery.py b/tests/click_tests/recovery.py index 55e9aaeee0..0e262a7e8d 100644 --- a/tests/click_tests/recovery.py +++ b/tests/click_tests/recovery.py @@ -41,7 +41,7 @@ def select_number_of_words( assert layout.text == "WordSelector" else: # Two title options - assert layout.get_title() in ("SEED CHECK", "RECOVERY MODE") + assert layout.get_title() in ("SEED CHECK", "WALLET RECOVERY") # click the number word_option_offset = 6 diff --git a/tests/persistence_tests/test_shamir_persistence.py b/tests/persistence_tests/test_shamir_persistence.py index 4cfb6aee5c..0944bc3a09 100644 --- a/tests/persistence_tests/test_shamir_persistence.py +++ b/tests/persistence_tests/test_shamir_persistence.py @@ -53,7 +53,7 @@ def test_abort(emulator: Emulator): assert layout.get_title() == "WALLET RECOVERY" layout = debug.click(buttons.OK, wait=True) - assert "Select number of words" in layout.text + assert "Select number of words" in layout.get_content() device_handler.restart(emulator) debug = device_handler.debuglink() @@ -63,7 +63,7 @@ def test_abort(emulator: Emulator): # no waiting for layout because layout doesn't change layout = debug.read_layout() - assert "Select number of words" in layout.text + assert "Select number of words" in layout.get_content() layout = debug.click(buttons.CANCEL, wait=True) assert layout.get_title() == "ABORT RECOVERY"