From c16800a9e0c10871ca3082d3b0c74cfbedc8965d Mon Sep 17 00:00:00 2001 From: obrusvit Date: Wed, 24 Apr 2024 23:45:11 +0200 Subject: [PATCH 1/4] feat(core/ui): add text align to T3T1 button --- .../src/ui/model_mercury/component/button.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/core/embed/rust/src/ui/model_mercury/component/button.rs b/core/embed/rust/src/ui/model_mercury/component/button.rs index f57414c89..eed4ce5cb 100644 --- a/core/embed/rust/src/ui/model_mercury/component/button.rs +++ b/core/embed/rust/src/ui/model_mercury/component/button.rs @@ -9,7 +9,7 @@ use crate::{ }, display::{self, toif::Icon, Color, Font}, event::TouchEvent, - geometry::{Alignment2D, Insets, Offset, Point, Rect}, + geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect}, shape, shape::Renderer, }, @@ -30,6 +30,7 @@ pub struct Button { touch_expand: Option, content: ButtonContent, styles: ButtonStyleSheet, + text_align: Alignment, state: State, long_press: Option, long_timer: Option, @@ -47,6 +48,7 @@ impl Button { area: Rect::zero(), touch_expand: None, styles: theme::button_default(), + text_align: Alignment::Start, state: State::Initial, long_press: None, long_timer: None, @@ -78,6 +80,11 @@ impl Button { self } + pub const fn with_text_align(mut self, align: Alignment) -> Self { + self.text_align = align; + self + } + pub const fn with_expanded_touch_area(mut self, expand: Insets) -> Self { self.touch_expand = Some(expand); self @@ -217,11 +224,19 @@ impl Button { match &self.content { ButtonContent::Empty => {} ButtonContent::Text(text) => { - let start_of_baseline = self.area.left_center() + Self::BASELINE_OFFSET; + let y_offset = Offset::y(self.style().font.allcase_text_height() / 2); + let start_of_baseline = match self.text_align { + Alignment::Start => { + self.area.left_center() + Offset::x(Self::BASELINE_OFFSET.x) + } + Alignment::Center => self.area.center(), + Alignment::End => self.area.right_center() - Offset::x(Self::BASELINE_OFFSET.x), + } + y_offset; text.map(|text| { shape::Text::new(start_of_baseline, text) .with_font(style.font) .with_fg(style.text_color) + .with_align(self.text_align) .render(target); }); } From e6a2d5d483167220a8b86c1530ff2162949312af Mon Sep 17 00:00:00 2001 From: obrusvit Date: Wed, 24 Apr 2024 23:46:02 +0200 Subject: [PATCH 2/4] feat(core/ui): T3T1 word count choice --- .../component/keyboard/word_count.rs | 19 +++++++++++----- .../rust/src/ui/model_mercury/theme/mod.rs | 22 +++++++++---------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/core/embed/rust/src/ui/model_mercury/component/keyboard/word_count.rs b/core/embed/rust/src/ui/model_mercury/component/keyboard/word_count.rs index bdccf58bf..fbda9ca84 100644 --- a/core/embed/rust/src/ui/model_mercury/component/keyboard/word_count.rs +++ b/core/embed/rust/src/ui/model_mercury/component/keyboard/word_count.rs @@ -1,6 +1,6 @@ use crate::ui::{ component::{Component, Event, EventCtx}, - geometry::{Grid, GridCellSpan, Rect}, + geometry::{Alignment, Grid, GridCellSpan, Rect}, model_mercury::{ component::button::{Button, ButtonMsg}, theme, @@ -10,7 +10,7 @@ use crate::ui::{ const NUMBERS: [u32; 5] = [12, 18, 20, 24, 33]; const LABELS: [&str; 5] = ["12", "18", "20", "24", "33"]; -const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)]; +const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (1, 0), (1, 2), (2, 1)]; pub struct SelectWordCount { button: [Button; NUMBERS.len()], @@ -23,7 +23,11 @@ pub enum SelectWordCountMsg { impl SelectWordCount { pub fn new() -> Self { SelectWordCount { - button: LABELS.map(|t| Button::with_text(t.into()).styled(theme::button_pin())), + button: LABELS.map(|t| { + Button::with_text(t.into()) + .styled(theme::button_pin()) + .with_text_align(Alignment::Center) + }), } } } @@ -32,8 +36,13 @@ impl Component for SelectWordCount { type Msg = SelectWordCountMsg; fn place(&mut self, bounds: Rect) -> Rect { - let (_, bounds) = bounds.split_bottom(2 * theme::BUTTON_HEIGHT + theme::BUTTON_SPACING); - let grid = Grid::new(bounds, 2, 6).with_spacing(theme::BUTTON_SPACING); + let n_rows: usize = 3; + let n_cols: usize = 4; + + let (_, bounds) = bounds.split_bottom( + n_rows as i16 * theme::BUTTON_HEIGHT + (n_rows as i16 - 1) * theme::BUTTON_SPACING, + ); + let grid = Grid::new(bounds, n_rows, n_cols).with_spacing(theme::BUTTON_SPACING); for (btn, (x, y)) in self.button.iter_mut().zip(CELLS) { btn.place(grid.cells(GridCellSpan { from: (x, y), diff --git a/core/embed/rust/src/ui/model_mercury/theme/mod.rs b/core/embed/rust/src/ui/model_mercury/theme/mod.rs index 93afbb921..48f28f81b 100644 --- a/core/embed/rust/src/ui/model_mercury/theme/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/theme/mod.rs @@ -388,22 +388,22 @@ pub const fn button_reset() -> ButtonStyleSheet { pub const fn button_pin() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { - font: Font::MONO, - text_color: FG, - button_color: GREY_DARK, + font: Font::NORMAL, + text_color: GREY_LIGHT, + button_color: GREY_EXTRA_DARK, icon_color: GREY_LIGHT, background_color: BG, }, active: &ButtonStyle { - font: Font::MONO, - text_color: FG, - button_color: GREY_MEDIUM, + font: Font::NORMAL, + text_color: GREY_LIGHT, + button_color: GREY_EXTRA_DARK, icon_color: GREY_LIGHT, background_color: BG, }, disabled: &ButtonStyle { - font: Font::MONO, - text_color: GREY_LIGHT, + font: Font::NORMAL, + text_color: GREY_DARK, button_color: BG, // so there is no "button" itself, just the text icon_color: GREY_LIGHT, background_color: BG, @@ -639,9 +639,9 @@ pub const TEXT_CHECKLIST_DONE: TextStyle = TextStyle::new(Font::NORMAL, GREEN_DARK, BG, GREY_LIGHT, GREY_LIGHT); pub const CONTENT_BORDER: i16 = 0; -pub const BUTTON_HEIGHT: i16 = 50; -pub const BUTTON_WIDTH: i16 = 56; -pub const BUTTON_SPACING: i16 = 6; +pub const BUTTON_HEIGHT: i16 = 62; +pub const BUTTON_WIDTH: i16 = 78; +pub const BUTTON_SPACING: i16 = 2; pub const KEYBOARD_SPACING: i16 = BUTTON_SPACING; pub const CHECKLIST_SPACING: i16 = 10; pub const RECOVERY_SPACING: i16 = 18; From 63965ea07fc7353906a0e63ca12de1d14168f453 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Thu, 25 Apr 2024 15:26:58 +0200 Subject: [PATCH 3/4] feat(core/ui): T3T1 mnemonic keyboard These are initial changes to the mnemonic keyboard designs. The designs themselves from Figma are still WIP. --- .../model_mercury/component/keyboard/bip39.rs | 9 +-- .../component/keyboard/mnemonic.rs | 57 ++++++++++-------- .../component/keyboard/slip39.rs | 6 +- .../src/ui/model_mercury/res/autofill30.png | Bin 542 -> 534 bytes .../src/ui/model_mercury/res/autofill30.toif | Bin 150 -> 158 bytes .../ui/model_mercury/res/confirm_input30.png | Bin 0 -> 526 bytes .../ui/model_mercury/res/confirm_input30.toif | Bin 0 -> 169 bytes .../src/ui/model_mercury/res/delete30.png | Bin 484 -> 389 bytes .../src/ui/model_mercury/res/delete30.toif | Bin 139 -> 117 bytes .../rust/src/ui/model_mercury/theme/mod.rs | 25 ++++---- 10 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 core/embed/rust/src/ui/model_mercury/res/confirm_input30.png create mode 100644 core/embed/rust/src/ui/model_mercury/res/confirm_input30.toif diff --git a/core/embed/rust/src/ui/model_mercury/component/keyboard/bip39.rs b/core/embed/rust/src/ui/model_mercury/component/keyboard/bip39.rs index 8aba67d34..81e01c2fc 100644 --- a/core/embed/rust/src/ui/model_mercury/component/keyboard/bip39.rs +++ b/core/embed/rust/src/ui/model_mercury/component/keyboard/bip39.rs @@ -196,7 +196,7 @@ impl Component for Bip39Input { let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0); shape::ToifImage::new(icon_center, icon.toif) .with_align(Alignment2D::CENTER) - .with_fg(style.text_color) + .with_fg(style.icon_color) .render(target); } } @@ -227,7 +227,8 @@ impl Bip39Input { // Styling the input to reflect already filled word Self { - button: Button::with_icon(theme::ICON_LIST_CHECK).styled(theme::button_pin_confirm()), + button: Button::with_icon(theme::ICON_CONFIRM_INPUT) + .styled(theme::button_pin_confirm()), textbox: TextBox::new(unwrap!(String::try_from(word))), multi_tap: MultiTapKeyboard::new(), options_num: bip39::options_num(word), @@ -293,7 +294,7 @@ impl Bip39Input { self.button.enable(ctx); self.button.set_stylesheet(ctx, theme::button_pin_confirm()); self.button - .set_content(ctx, ButtonContent::Icon(theme::ICON_LIST_CHECK)); + .set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM_INPUT)); self.button_suggestion .set_stylesheet(ctx, theme::button_suggestion_confirm()); } else { @@ -302,7 +303,7 @@ impl Bip39Input { self.button .set_stylesheet(ctx, theme::button_pin_autocomplete()); self.button - .set_content(ctx, ButtonContent::Icon(theme::ICON_CLICK)); + .set_content(ctx, ButtonContent::Icon(theme::ICON_AUTOFILL)); self.button_suggestion .set_stylesheet(ctx, theme::button_suggestion_autocomplete()); } diff --git a/core/embed/rust/src/ui/model_mercury/component/keyboard/mnemonic.rs b/core/embed/rust/src/ui/model_mercury/component/keyboard/mnemonic.rs index def0f6897..839dbbcd9 100644 --- a/core/embed/rust/src/ui/model_mercury/component/keyboard/mnemonic.rs +++ b/core/embed/rust/src/ui/model_mercury/component/keyboard/mnemonic.rs @@ -2,7 +2,7 @@ use crate::{ strutil::TString, ui::{ component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe}, - geometry::{Alignment2D, Grid, Offset, Rect}, + geometry::{Alignment, Grid, Rect}, model_mercury::{ component::{Button, ButtonMsg, Swipe, SwipeDirection}, theme, @@ -44,23 +44,23 @@ where Self { prompt: Child::new(Maybe::new( theme::BG, - Label::centered(prompt, theme::label_keyboard_prompt()), + Label::centered(prompt, theme::TEXT_MAIN_GREY_LIGHT), prompt_visible, )), back: Child::new(Maybe::new( theme::BG, - Button::with_icon_blend( - theme::IMAGE_BG_BACK_BTN_TALL, - theme::ICON_BACK, - Offset::new(30, 17), - ) - .styled(theme::button_reset()) - .with_long_press(theme::ERASE_HOLD_DURATION), + Button::with_icon(theme::ICON_DELETE) + .styled(theme::button_default()) + .with_long_press(theme::ERASE_HOLD_DURATION), !prompt_visible, )), input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)), keys: T::keys() - .map(|t| Button::with_text(t.into()).styled(theme::button_pin())) + .map(|t| { + Button::with_text(t.into()) + .styled(theme::button_pin()) + .with_text_align(Alignment::Center) + }) .map(Child::new), swipe: Swipe::new().right(), can_go_back, @@ -109,23 +109,27 @@ where type Msg = MnemonicKeyboardMsg; fn place(&mut self, bounds: Rect) -> Rect { - 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)); - - let prompt_center = grid.row_col(0, 0).union(grid.row_col(0, 3)).center(); - let prompt_size = self.prompt.inner().inner().max_size(); - let prompt_area = Rect::snap(prompt_center, prompt_size, Alignment2D::CENTER); + let height_input_area: i16 = 30; + let space_top: i16 = 8; + let (remaining, keyboard_area) = + bounds.split_bottom(3 * theme::BUTTON_HEIGHT + 2 * theme::KEYBOARD_SPACING); + let prompt_area = remaining + .split_top(space_top) + .1 + .split_top(height_input_area) + .0; + assert!(prompt_area.height() == height_input_area); + + let (back_btn_area, input_area) = prompt_area.split_left(30); + let keyboard_grid = Grid::new(keyboard_area, 3, 3).with_spacing(theme::KEYBOARD_SPACING); self.swipe.place(bounds); self.prompt.place(prompt_area); - self.back.place(back_area); + self.back.place(back_btn_area); self.input.place(input_area); + for (key, btn) in self.keys.iter_mut().enumerate() { - btn.place(grid.cell(key + grid.cols)); // Start in the second row. + btn.place(keyboard_grid.cell(key)); } bounds } @@ -185,9 +189,12 @@ where } fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { - self.prompt.render(target); - self.input.render(target); - self.back.render(target); + if self.input.inner().inner().is_empty() { + self.prompt.render(target); + } else { + self.input.render(target); + self.back.render(target); + } for btn in &self.keys { btn.render(target); diff --git a/core/embed/rust/src/ui/model_mercury/component/keyboard/slip39.rs b/core/embed/rust/src/ui/model_mercury/component/keyboard/slip39.rs index 2aba48275..5160e7ddc 100644 --- a/core/embed/rust/src/ui/model_mercury/component/keyboard/slip39.rs +++ b/core/embed/rust/src/ui/model_mercury/component/keyboard/slip39.rs @@ -181,7 +181,7 @@ impl Component for Slip39Input { icon.draw( icon_center, Alignment2D::CENTER, - style.text_color, + style.icon_color, style.button_color, ); } @@ -247,7 +247,7 @@ impl Component for Slip39Input { let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0); shape::ToifImage::new(icon_center, icon.toif) .with_align(Alignment2D::CENTER) - .with_fg(style.text_color) + .with_fg(style.icon_color) .render(target); } } @@ -356,7 +356,7 @@ impl Slip39Input { // Confirm button. self.button.enable(ctx); self.button - .set_content(ctx, ButtonContent::Icon(theme::ICON_LIST_CHECK)); + .set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM_INPUT)); } else { // Disabled button. self.button.disable(ctx); diff --git a/core/embed/rust/src/ui/model_mercury/res/autofill30.png b/core/embed/rust/src/ui/model_mercury/res/autofill30.png index 16cd93f3aad96f9335cac1ec45721598e13f22e5..67c67a7b7a1c218a560dfda11de97da751390b86 100644 GIT binary patch literal 534 zcmV+x0_pvUP)D9KhX2`EP=*t@bar58CloyF8Kg`0P2(A5aBu(uuK!8%7w-&s$Z3MIrs7m~lLZX*chF+* zxY5p7qDJ-SYm*5(ZR#PHDKSS6H7wRzc+jHWzaEvV$5Z!6^N`aw%rh(&@;21YqGU&d zdMr-`xJu5XNuu+R(=Oe_w_eA<$dpTz;(5wjBGy^fbrZ5809b-5YWLrkGB$hE>T5V{ z9nS8Rv7JMt*z;#f_qpu?iQzrF5Qk-?r(k({n@6TRgx@x)ZiglzEBw@HTm^>h={o4V z94KQObIS#QzDXZZQYbL(5WwK99b;!9YQ&C9ZWXuvHFPnx8)+1fa|JfWT9sQJ3K#(# z-EC*7dBvWr_#%m7YBv?l05xU$VQ|_R$%2Bq;b(MImWM9RiSFqh#0oDqHPS911+`;u zUYTmKsvU8cxl07zY-L@f7T}2c9ePTWa?Kjai>qC|BQEQ*PDPl2+{2?48@I$+j$A_g Y0S!urym_a7t^fc407*qoM6N<$fG*lJ~1#b>Up|2hG?8`owTs{kby|M{iP!Jqb|zZcKiA?I5Zr&Ce}DNA+PL@ zZNwRt$+I0w$~zRpIg^^WTqdmaIa)S5!`tXa-uWl7`=`FU(5V0b diff --git a/core/embed/rust/src/ui/model_mercury/res/autofill30.toif b/core/embed/rust/src/ui/model_mercury/res/autofill30.toif index 5551d07fe6bcd8545ed1882ac51ff61596a80bc0..712fb68e25660d9a15a1ab494adf9a493f1151c4 100644 GIT binary patch literal 158 zcmV;P0Ac@BPf14}03HC60001EU?3GZlyzhX!+D7vARr3m8FWAZGlbg$<$9nBu57W0Pd|ZTL1t6 literal 150 zcmV;H0BQeJPf14_02%;_0001EU_b*7WgQv9PrL!Z5`<>=;6lz E073jTQvd(} diff --git a/core/embed/rust/src/ui/model_mercury/res/confirm_input30.png b/core/embed/rust/src/ui/model_mercury/res/confirm_input30.png new file mode 100644 index 0000000000000000000000000000000000000000..dc7148c8c6447b2897a02379ed7ea884ccc10c4e GIT binary patch literal 526 zcmV+p0`dKcP)3I94N=Z5u}n`*EIlQ7+@s+|jp~g*1jWpG+7qhF;OH~`Mtp&~0OA_GntZ2^;$0ZxX=0f5{nT(+B#-kxq_(p@-! z$x#W;(*d$_`ch{4PF>m4RDGis>L41boHaHYER*@c0xD@96TUeqDA-60I>(BHGAt7lk*zvA0sAtTZE{B Q2LJ#707*qoM6N<$f}eWceEl!Guoo+T!kOite0uJp8@ zzSRhx@qeMDVx5x`i2JXcoBgYqfH#5vPV}JVS{DMm3H$>uj|JO!bp-D>D5xApa25I= zBec+ptjjX3D7q{f$*w|mARL~q6+pe<4OnUeP}||EH-$o(0%*hXhPui?{1gh{Ez*6T z)d^;-%L9;6Xv{8=(f07~U2XbN3eU-Pj=6|zd)Myn5}aK`JZl|JoOo>pgBy$4hE^U` zg6XXjM!A!G@)kIgMF$UVLn?v2~60+H?6_pOIhvlP4g=zpPu~v`@h{CdyjGMoh8-r$NBs* zkFEWzrxv$gWOVX>!XXozlGsmPsx?{~(?sxNi&t2zn;BQ&j zXqw4aC#9OD>e;D%;2vYZ@!*SXXMb@ViTWni%CKNt_~8aK%ZBXr*8X3AizV!2sSbWr zHa*sM-}CqLLKBQ)Sv;kp(HamwYVfPw*XC#iMf@b0mPD- T`m^GJdKf%i{an^LB{Ts5DgUyS diff --git a/core/embed/rust/src/ui/model_mercury/res/delete30.toif b/core/embed/rust/src/ui/model_mercury/res/delete30.toif index 925396407b17a55886b1f3c832d5b2c3ae9b92d5..eea1cf0df2d0ae34ab434f6ce7ced7b2d81a6d42 100644 GIT binary patch literal 117 zcmV-*0E+)qPf14}03HBo0001EV4x{z=>P*W2rr=n3RuA0b{JO!#4CXDIx>Jf0idLZ zK}QEO&;%g25Gc&S&}_lb0OEoqJ1iIsm>HTu+(NJ}W(EeJ`VN?}77QRsOfH&YG^5Zg XLU#t5Baysd!GI#og5rHOS_Uxy^(-mU literal 139 zcmV;60CfLUPf14`02Ba!0000@&#?*uF%SgMAtu`T34)-OK^tqU^jf5}yi%}9?V&fH z?ad}=rdb}dfh+)QP8J8{&YHdOXyBm-S3T$q=QoGvJPa9Q#*6f}w!{^YO!ZBGz#E%{ t^f6>$oQIzpqy%X|43al3BEwphR4(LhQf1x$zdsf9a-B-O`DpiVfiKp*K9>Lh diff --git a/core/embed/rust/src/ui/model_mercury/theme/mod.rs b/core/embed/rust/src/ui/model_mercury/theme/mod.rs index 48f28f81b..1aeb18bc6 100644 --- a/core/embed/rust/src/ui/model_mercury/theme/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/theme/mod.rs @@ -88,6 +88,7 @@ include_icon!(ICON_WARNING, "model_mercury/res/warning24.toif"); // 30x30 include_icon!(ICON_AUTOFILL, "model_mercury/res/autofill30.toif"); include_icon!(ICON_CLOSE, "model_mercury/res/close30.toif"); +include_icon!(ICON_CONFIRM_INPUT, "model_mercury/res/confirm_input30.toif"); include_icon!(ICON_DELETE, "model_mercury/res/delete30.toif"); include_icon!(ICON_MENU, "model_mercury/res/menu30.toif"); include_icon!( @@ -411,27 +412,29 @@ pub const fn button_pin() -> ButtonStyleSheet { } } +// TODO: will button_pin_xyz styles be the same for PIN and Mnemonic keyboard? +// Wait for Figma pub const fn button_pin_confirm() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { font: Font::MONO, text_color: FG, - button_color: GREEN, - icon_color: GREY_LIGHT, + button_color: BG, + icon_color: GREEN_LIGHT, background_color: BG, }, active: &ButtonStyle { font: Font::MONO, text_color: FG, - button_color: GREEN_DARK, - icon_color: GREY_LIGHT, + button_color: GREY_DARK, + icon_color: GREEN_LIGHT, background_color: BG, }, disabled: &ButtonStyle { font: Font::MONO, - text_color: GREY_LIGHT, - button_color: GREY_DARK, - icon_color: GREY_LIGHT, + text_color: FG, + button_color: BG, + icon_color: GREEN_LIGHT, background_color: BG, }, } @@ -442,20 +445,20 @@ pub const fn button_pin_autocomplete() -> ButtonStyleSheet { normal: &ButtonStyle { font: Font::MONO, text_color: FG, - button_color: GREY_DARK, // same as PIN buttons + button_color: BG, icon_color: GREY_LIGHT, background_color: BG, }, active: &ButtonStyle { font: Font::MONO, text_color: FG, - button_color: GREEN_DARK, + button_color: BG, icon_color: GREY_LIGHT, background_color: BG, }, disabled: &ButtonStyle { font: Font::MONO, - text_color: GREY_LIGHT, + text_color: FG, button_color: BG, icon_color: GREY_LIGHT, background_color: BG, @@ -467,7 +470,7 @@ pub const fn button_suggestion_confirm() -> ButtonStyleSheet { ButtonStyleSheet { normal: &ButtonStyle { font: Font::MONO, - text_color: GREEN_DARK, + text_color: GREY_LIGHT, button_color: GREEN, icon_color: GREY_LIGHT, background_color: BG, From 3628630584099233d807fc8762584d40d62dafa7 Mon Sep 17 00:00:00 2001 From: obrusvit Date: Thu, 25 Apr 2024 23:41:16 +0200 Subject: [PATCH 4/4] feat(core/ui): improve T3T1 recovery --- core/embed/rust/librust_qstr.h | 1 + .../flow/confirm_reset_device.rs | 11 +-- .../embed/rust/src/ui/model_mercury/layout.rs | 79 ++++++++++--------- core/mocks/generated/trezorui2.pyi | 5 ++ .../src/trezor/ui/layouts/mercury/__init__.py | 42 ++++++---- .../src/trezor/ui/layouts/mercury/recovery.py | 4 +- core/src/trezor/ui/layouts/mercury/reset.py | 5 +- 7 files changed, 76 insertions(+), 71 deletions(-) diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h index f8d306fb9..5eb4d0d5b 100644 --- a/core/embed/rust/librust_qstr.h +++ b/core/embed/rust/librust_qstr.h @@ -152,6 +152,7 @@ static void _librust_qstrs(void) { MP_QSTR_confirm_backup_written_down; MP_QSTR_confirm_blob; MP_QSTR_confirm_coinjoin; + MP_QSTR_confirm_create_wallet; MP_QSTR_confirm_emphasized; MP_QSTR_confirm_fido; MP_QSTR_confirm_firmware_update; diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs index 66381204e..c03a868fc 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_device.rs @@ -22,7 +22,6 @@ use super::super::{ pub enum ConfirmResetDevice { Intro, Menu, - Confirm, } impl FlowState for ConfirmResetDevice { @@ -34,12 +33,7 @@ impl FlowState for ConfirmResetDevice { (ConfirmResetDevice::Menu, SwipeDirection::Right) => { Decision::Goto(ConfirmResetDevice::Intro, direction) } - (ConfirmResetDevice::Intro, SwipeDirection::Up) => { - Decision::Goto(ConfirmResetDevice::Confirm, direction) - } - (ConfirmResetDevice::Confirm, SwipeDirection::Down) => { - Decision::Goto(ConfirmResetDevice::Intro, direction) - } + (ConfirmResetDevice::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed), _ => Decision::Nothing, } } @@ -53,9 +47,6 @@ impl FlowState for ConfirmResetDevice { Decision::Goto(ConfirmResetDevice::Intro, SwipeDirection::Right) } (ConfirmResetDevice::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), - (ConfirmResetDevice::Confirm, FlowMsg::Confirmed) => { - Decision::Return(FlowMsg::Confirmed) - } _ => Decision::Nothing, } } diff --git a/core/embed/rust/src/ui/model_mercury/layout.rs b/core/embed/rust/src/ui/model_mercury/layout.rs index 145798bbc..2e3e8b785 100644 --- a/core/embed/rust/src/ui/model_mercury/layout.rs +++ b/core/embed/rust/src/ui/model_mercury/layout.rs @@ -192,6 +192,15 @@ impl ComponentMsgObj for ShareWords<'_> { } } +impl ComponentMsgObj for SelectWordCount { + fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { + match msg { + SelectWordCountMsg::Selected(n) => n.try_into(), + _ => Err(Error::TypeError), + } + } +} + impl ComponentMsgObj for VerticalMenu { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { match msg { @@ -758,6 +767,18 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs: unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } } +extern "C" fn new_confirm_create_wallet(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { + let block = move |_args: &[Obj], _kwargs: &Map| { + let content = PromptScreen::new_hold_to_confirm(); + let obj = LayoutObj::new( + Frame::left_aligned(TR::reset__title_create_wallet.into(), content) + .with_footer(TR::instructions__hold_to_confirm.into(), None), + )?; + Ok(obj.into()) + }; + unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } +} + extern "C" fn new_confirm_value(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; @@ -1331,8 +1352,9 @@ extern "C" fn new_confirm_backup_written_down( args: *const Obj, kwargs: *mut Map, ) -> Obj { - let block = move |_args: &[Obj], kwargs: &Map| { + let block = move |_args: &[Obj], _kwargs: &Map| { let content = PromptScreen::new_hold_to_confirm(); + // TODO: use TR let frame_with_hold_to_confirm = Frame::left_aligned("I wrote down all words in order.".into(), content) .with_footer(TR::instructions__hold_to_confirm.into(), None); @@ -1413,17 +1435,13 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; + let _title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?; let description: TString = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?; - let button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; + let _button: TString = kwargs.get(Qstr::MP_QSTR_button)?.try_into()?; let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?; - let info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?; + let _info_button: bool = kwargs.get_or(Qstr::MP_QSTR_info_button, false)?; - let paragraphs = Paragraphs::new([ - Paragraph::new(&theme::TEXT_DEMIBOLD, title), - Paragraph::new(&theme::TEXT_NORMAL, description), - ]) - .with_spacing(theme::RECOVERY_SPACING); + let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]); let notification: TString = if dry_run { TR::recovery__title_dry_run.try_into()? @@ -1431,23 +1449,12 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut TR::recovery__title.try_into()? }; - let obj = if info_button { - LayoutObj::new(Frame::left_aligned( - notification, - Dialog::new( - paragraphs, - Button::cancel_info_confirm( - TR::buttons__continue.into(), - TR::buttons__more_info.into(), - ), - ), - ))? - } else { - LayoutObj::new(Frame::left_aligned( - notification, - Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))), - ))? - }; + let content = SwipeUpScreen::new(paragraphs); + let obj = LayoutObj::new( + Frame::left_aligned(notification, content) + .with_footer(TR::instructions__swipe_up.into(), None) + .with_subtitle("Instructions".into()), // FIXME: use TR + )?; Ok(obj.into()) }; unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) } @@ -1455,21 +1462,11 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { let block = move |_args: &[Obj], kwargs: &Map| { - let dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?; - let title: TString = if dry_run { - TR::recovery__title_dry_run.try_into()? - } else { - TR::recovery__title.try_into()? - }; - - let paragraphs = Paragraphs::new(Paragraph::new( - &theme::TEXT_DEMIBOLD, - TR::recovery__select_num_of_words, - )); + let _dry_run: bool = kwargs.get(Qstr::MP_QSTR_dry_run)?.try_into()?; let obj = LayoutObj::new(Frame::left_aligned( - title, - Dialog::new(paragraphs, SelectWordCount::new()), + TR::recovery__select_num_of_words.into(), + SelectWordCount::new(), ))?; Ok(obj.into()) }; @@ -1825,6 +1822,10 @@ pub static mp_module_trezorui2: Module = obj_module! { /// """Confirm TOS before device setup.""" Qstr::MP_QSTR_confirm_reset_device => obj_fn_kw!(0, flow::confirm_reset_device::new_confirm_reset_device).as_obj(), + /// def confirm_create_wallet() -> LayoutObj[UiResult]: + /// """Confirm creating wallet""" + Qstr::MP_QSTR_confirm_create_wallet => obj_fn_kw!(0, new_confirm_create_wallet).as_obj(), + /// def show_address_details( /// *, /// qr_title: str, diff --git a/core/mocks/generated/trezorui2.pyi b/core/mocks/generated/trezorui2.pyi index d03a58cdc..6e16c11c9 100644 --- a/core/mocks/generated/trezorui2.pyi +++ b/core/mocks/generated/trezorui2.pyi @@ -154,6 +154,11 @@ def confirm_reset_device( """Confirm TOS before device setup.""" +# rust/src/ui/model_mercury/layout.rs +def confirm_create_wallet() -> LayoutObj[UiResult]: + """Confirm creating wallet""" + + # rust/src/ui/model_mercury/layout.rs def show_address_details( *, diff --git a/core/src/trezor/ui/layouts/mercury/__init__.py b/core/src/trezor/ui/layouts/mercury/__init__.py index af012cfef..d33eda0cf 100644 --- a/core/src/trezor/ui/layouts/mercury/__init__.py +++ b/core/src/trezor/ui/layouts/mercury/__init__.py @@ -228,11 +228,12 @@ def draw_simple(layout: Any) -> None: ui.backlight_fade(ui.style.BACKLIGHT_NORMAL) -async def raise_if_not_confirmed(a: Awaitable[T], exc: Any = ActionCancelled) -> T: +async def raise_if_not_confirmed( + a: Awaitable[ui.UiResult], exc: Any = ActionCancelled +) -> None: result = await a if result is not CONFIRMED: raise exc - return result async def confirm_action( @@ -322,22 +323,36 @@ async def confirm_single( async def confirm_reset_device(title: str, recovery: bool = False) -> None: - await raise_if_not_confirmed( - interact( + if recovery: + await raise_if_not_confirmed( + interact( + RustLayout( + trezorui2.confirm_reset_device( + title=title, + button="", # not used + ) + ), + "recover_device", + ButtonRequestType.ProtectCall, + ) + ) + else: + # creating wallet shows TOS first and then an extra screen confirms + await raise_if_not_confirmed( RustLayout( trezorui2.confirm_reset_device( title=title, button="", # not used ) ), - "recover_device" if recovery else "setup_device", - ( - ButtonRequestType.ProtectCall - if recovery - else ButtonRequestType.ResetDevice - ), ) - ) + await raise_if_not_confirmed( + interact( + RustLayout(trezorui2.confirm_create_wallet()), + "setup_device", + ButtonRequestType.ResetDevice, + ) + ) async def prompt_backup() -> bool: @@ -565,15 +580,12 @@ async def show_success( subheader: str | None = None, button: str | None = None, ) -> None: - button = button or TR.buttons__continue # def_arg await raise_if_not_confirmed( interact( RustLayout( trezorui2.show_success( - title=subheader or "", + title=content, description="", - button=button.upper(), - allow_cancel=False, ) ), br_type, diff --git a/core/src/trezor/ui/layouts/mercury/recovery.py b/core/src/trezor/ui/layouts/mercury/recovery.py index 10c8f3aa5..410434d10 100644 --- a/core/src/trezor/ui/layouts/mercury/recovery.py +++ b/core/src/trezor/ui/layouts/mercury/recovery.py @@ -160,9 +160,7 @@ async def show_recovery_warning( RustLayout( trezorui2.show_warning( title=content, - description=subheader or "", - button=button.upper(), - allow_cancel=False, + value="Try again", # TODO: use TR ) ), br_type, diff --git a/core/src/trezor/ui/layouts/mercury/reset.py b/core/src/trezor/ui/layouts/mercury/reset.py index 337ab74d4..a6025edc2 100644 --- a/core/src/trezor/ui/layouts/mercury/reset.py +++ b/core/src/trezor/ui/layouts/mercury/reset.py @@ -41,10 +41,7 @@ async def show_share_words( result = await interact( RustLayout( - trezorui2.confirm_backup_written_down( - title=title, - pages=share_words, - ), + trezorui2.confirm_backup_written_down(), ), "backup_words", ButtonRequestType.ResetDevice,