diff --git a/core/assets/back_btn.png b/core/assets/back_btn.png new file mode 100644 index 000000000..f7e591f69 Binary files /dev/null and b/core/assets/back_btn.png differ diff --git a/core/assets/back_btn_tall.png b/core/assets/back_btn_tall.png new file mode 100644 index 000000000..8e054340f Binary files /dev/null and b/core/assets/back_btn_tall.png differ diff --git a/core/embed/rust/src/ui/component/image.rs b/core/embed/rust/src/ui/component/image.rs index d5ae25476..1538cc964 100644 --- a/core/embed/rust/src/ui/component/image.rs +++ b/core/embed/rust/src/ui/component/image.rs @@ -79,7 +79,6 @@ impl BlendedImage { } } - #[cfg(feature = "dma2d")] fn paint_image(&self) { display::icon_over_icon( None, @@ -88,17 +87,6 @@ impl BlendedImage { self.area_color, ); } - - #[cfg(not(feature = "dma2d"))] - fn paint_image(&self) { - display::icon_top_left(self.bg_top_left, self.bg, self.bg_color, self.area_color); - display::icon_top_left( - self.bg_top_left + self.fg_offset, - self.fg, - self.fg_color, - self.bg_color, - ); - } } impl Component for BlendedImage { diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index 2c8372bd4..00a0cb81e 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -748,6 +748,27 @@ pub fn icon_over_icon( dma2d_wait_for_transfer(); } +#[cfg(not(feature = "dma2d"))] +pub fn icon_over_icon( + bg_area: Option, + bg: (&[u8], Offset, Color), + fg: (&[u8], Offset, Color), + bg_color: Color, +) { + let (data_bg, offset_bg, color_icon_bg) = bg; + let (data_fg, offset_fg, color_icon_fg) = fg; + + let pos_bg = if let Some(area) = bg_area { + rect_fill(area, bg_color); + area.top_left() + offset_bg + } else { + Point::from(offset_bg) + }; + + icon_top_left(pos_bg, data_bg, color_icon_bg, bg_color); + icon_top_left(pos_bg + offset_fg, data_fg, color_icon_fg, color_icon_bg); +} + /// Gets a color of a pixel on `p` coordinates of rounded rectangle with corner /// radius 2 fn rect_rounded2_get_pixel( diff --git a/core/embed/rust/src/ui/model_tr/component/result_popup.rs b/core/embed/rust/src/ui/model_tr/component/result_popup.rs index 77a4aa5d6..979e2a783 100644 --- a/core/embed/rust/src/ui/model_tr/component/result_popup.rs +++ b/core/embed/rust/src/ui/model_tr/component/result_popup.rs @@ -6,7 +6,6 @@ use crate::{ Child, Component, ComponentExt, Event, EventCtx, Label, Pad, }, constant::screen, - display::Font, geometry::{Alignment, Insets, LinearPlacement, Point, Rect}, model_tr::{ component::{Button, ButtonMsg, ButtonPos, ResultAnim, ResultAnimMsg}, diff --git a/core/embed/rust/src/ui/model_tt/component/button.rs b/core/embed/rust/src/ui/model_tt/component/button.rs index 9638876ad..1c0864509 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -52,6 +52,10 @@ impl Button { Self::new(ButtonContent::Icon(image)) } + pub fn with_icon_blend(bg: &'static [u8], fg: &'static [u8], fg_offset: Offset) -> Self { + Self::new(ButtonContent::IconBlend(bg, fg, fg_offset)) + } + pub fn empty() -> Self { Self::new(ButtonContent::Empty) } @@ -141,29 +145,34 @@ impl Button { } pub fn paint_background(&self, style: &ButtonStyle) { - if style.border_width > 0 { - // Paint the border and a smaller background on top of it. - display::rect_fill_rounded( - self.area, - style.border_color, - style.background_color, - style.border_radius, - ); - display::rect_fill_rounded( - self.area.inset(Insets::uniform(style.border_width)), - style.button_color, - style.border_color, - style.border_radius, - ); - } else { - // We do not need to draw an explicit border in this case, just a - // bigger background. - display::rect_fill_rounded( - self.area, - style.button_color, - style.background_color, - style.border_radius, - ); + match &self.content { + ButtonContent::IconBlend(_, _, _) => {} + _ => { + if style.border_width > 0 { + // Paint the border and a smaller background on top of it. + display::rect_fill_rounded( + self.area, + style.border_color, + style.background_color, + style.border_radius, + ); + display::rect_fill_rounded( + self.area.inset(Insets::uniform(style.border_width)), + style.button_color, + style.border_color, + style.border_radius, + ); + } else { + // We do not need to draw an explicit border in this case, just a + // bigger background. + display::rect_fill_rounded( + self.area, + style.button_color, + style.background_color, + style.border_radius, + ); + } + } } } @@ -196,6 +205,12 @@ impl Button { style.button_color, ); } + ButtonContent::IconBlend(bg, fg, offset) => display::icon_over_icon( + Some(self.area), + (bg, Offset::zero(), style.button_color), + (fg, *offset, style.text_color), + style.background_color, + ), } } } @@ -300,6 +315,7 @@ where ButtonContent::Empty => {} ButtonContent::Text(text) => t.field("text", text), ButtonContent::Icon(_) => t.symbol("icon"), + ButtonContent::IconBlend(_, _, _) => t.symbol("icon"), } t.close(); } @@ -318,6 +334,7 @@ pub enum ButtonContent { Empty, Text(T), Icon(&'static [u8]), + IconBlend(&'static [u8], &'static [u8], Offset), } #[derive(PartialEq, Eq)] 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 a8f9a9a10..de0f31175 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 @@ -2,7 +2,7 @@ use core::ops::Deref; use crate::ui::{ component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe}, - geometry::{Alignment, Grid, Rect}, + geometry::{Alignment, Grid, Offset, Rect}, model_tt::{ component::{Button, ButtonMsg}, theme, @@ -39,7 +39,12 @@ where )), back: Child::new(Maybe::hidden( theme::BG, - Button::with_icon(theme::ICON_BACK).styled(theme::button_clear()), + Button::with_icon_blend( + theme::IMAGE_BG_BACK_BTN_TALL, + theme::ICON_BACK, + Offset::new(30, 17), + ) + .styled(theme::button_clear()), )), input: Child::new(Maybe::hidden(theme::BG, input)), keys: T::keys().map(Button::with_text).map(Child::new), 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 117e8795c..4b39a930b 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 @@ -4,7 +4,10 @@ use crate::ui::{ geometry::{Grid, Insets, Offset, Rect}, model_tt::component::{ button::{Button, ButtonContent, ButtonMsg::Clicked}, - keyboard::common::{paint_pending_marker, MultiTapKeyboard, TextBox, HEADER_PADDING_SIDE}, + keyboard::common::{ + paint_pending_marker, MultiTapKeyboard, TextBox, HEADER_HEIGHT, HEADER_PADDING_BOTTOM, + HEADER_PADDING_SIDE, + }, swipe::{Swipe, SwipeDirection}, theme, ScrollBar, }, @@ -46,10 +49,14 @@ impl PassphraseKeyboard { confirm: Button::with_icon(theme::ICON_CONFIRM) .styled(theme::button_confirm()) .into_child(), - back: Button::with_icon(theme::ICON_BACK) - .styled(theme::button_reset()) - .initially_enabled(false) - .into_child(), + back: Button::with_icon_blend( + theme::IMAGE_BG_BACK_BTN, + theme::ICON_BACK, + Offset::new(30, 12), + ) + .styled(theme::button_reset()) + .initially_enabled(false) + .into_child(), keys: KEYBOARD.map(|page| { page.map(|text| { if text == " " { @@ -70,6 +77,7 @@ impl PassphraseKeyboard { ButtonContent::Text(text) => text, ButtonContent::Icon(_) => " ", ButtonContent::Empty => "", + ButtonContent::IconBlend(_, _, _) => "", } } @@ -135,17 +143,16 @@ impl Component for PassphraseKeyboard { fn place(&mut self, bounds: Rect) -> Rect { let bounds = bounds.inset(theme::borders()); - let input_area = Grid::new(bounds, 5, 1) - .with_spacing(theme::KEYBOARD_SPACING) - .row_col(0, 0); + let (input_area, key_grid_area) = bounds.split_top(HEADER_HEIGHT + HEADER_PADDING_BOTTOM); - let (input_area, scroll_area) = input_area.split_bottom(ScrollBar::DOT_SIZE); - let input_area = - input_area.inset(Insets::new(0, HEADER_PADDING_SIDE, 2, HEADER_PADDING_SIDE)); + let (input_area, scroll_area) = + input_area.split_bottom(ScrollBar::DOT_SIZE + theme::KEYBOARD_SPACING); + 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(bounds, 5, 3).with_spacing(theme::KEYBOARD_SPACING); - let confirm_btn_area = key_grid.cell(14); - let back_btn_area = key_grid.cell(12); + let key_grid = Grid::new(key_grid_area, 4, 3).with_spacing(theme::KEYBOARD_SPACING); + let confirm_btn_area = key_grid.cell(11); + let back_btn_area = key_grid.cell(9); self.page_swipe.place(bounds); self.input.place(input_area); @@ -162,10 +169,10 @@ impl Component for PassphraseKeyboard { // from the second row. let area = key_grid.cell(if key < 9 { // The grid has 3 columns, and we skip the first row. - key + 3 + key } else { // For the last key (the "0" position) we skip one cell. - key + 1 + 3 + key + 1 }); btn.place(area); } @@ -280,10 +287,11 @@ impl Component for Input { } fn paint(&mut self) { - const TEXT_OFFSET: Offset = Offset::y(8); - let style = theme::label_default(); - let mut text_baseline = self.area.bottom_left() - TEXT_OFFSET; + + let mut text_baseline = self.area.top_left() + Offset::y(style.text_font.text_height()) + - Offset::y(style.text_font.text_baseline()); + let text = self.textbox.content(); // Preparing the new text to be displayed. 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 ca9b7a52b..a16bbb511 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 @@ -70,10 +70,14 @@ where allow_cancel: bool, ) -> Self { // Control buttons. - let erase_btn = Button::with_icon(theme::ICON_BACK) - .styled(theme::button_reset()) - .with_long_press(ERASE_HOLD_DURATION) - .initially_enabled(false); + let erase_btn = Button::with_icon_blend( + theme::IMAGE_BG_BACK_BTN, + theme::ICON_BACK, + Offset::new(30, 12), + ) + .styled(theme::button_reset()) + .with_long_press(ERASE_HOLD_DURATION) + .initially_enabled(false); let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child(); let cancel_btn = Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel()); diff --git a/core/embed/rust/src/ui/model_tt/res/back_btn.toif b/core/embed/rust/src/ui/model_tt/res/back_btn.toif new file mode 100644 index 000000000..2a7953957 Binary files /dev/null and b/core/embed/rust/src/ui/model_tt/res/back_btn.toif differ diff --git a/core/embed/rust/src/ui/model_tt/res/back_btn_tall.toif b/core/embed/rust/src/ui/model_tt/res/back_btn_tall.toif new file mode 100644 index 000000000..452371857 Binary files /dev/null and b/core/embed/rust/src/ui/model_tt/res/back_btn_tall.toif differ diff --git a/core/embed/rust/src/ui/model_tt/theme.rs b/core/embed/rust/src/ui/model_tt/theme.rs index 2e2e01c6a..572ec9b09 100644 --- a/core/embed/rust/src/ui/model_tt/theme.rs +++ b/core/embed/rust/src/ui/model_tt/theme.rs @@ -65,6 +65,8 @@ pub const IMAGE_FG_ERROR: &[u8] = include_res!("model_tt/res/error_fg.toif"); pub const IMAGE_FG_INFO: &[u8] = include_res!("model_tt/res/info_fg.toif"); pub const IMAGE_BG_CIRCLE: &[u8] = include_res!("model_tt/res/circle.toif"); pub const IMAGE_BG_TRIANGLE: &[u8] = include_res!("model_tt/res/triangle.toif"); +pub const IMAGE_BG_BACK_BTN: &[u8] = include_res!("model_tt/res/back_btn.toif"); +pub const IMAGE_BG_BACK_BTN_TALL: &[u8] = include_res!("model_tt/res/back_btn_tall.toif"); // Scrollbar/PIN dots. pub const DOT_ACTIVE: &[u8] = include_res!("model_tt/res/scroll-active.toif"); @@ -229,7 +231,7 @@ pub fn button_reset() -> ButtonStyleSheet { }, disabled: &ButtonStyle { font: Font::BOLD, - text_color: GREY_LIGHT, + text_color: FG, button_color: YELLOW, background_color: BG, border_color: BG, diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 3be30fb46..0e158cb0f 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -1646,10 +1646,10 @@ "TT_test_session_id_and_passphrase.py::test_multiple_passphrases": "7f73c410413a655054ab7fcc95d9da528bd082a89cda3ee981c4c48b566e6660", "TT_test_session_id_and_passphrase.py::test_multiple_sessions": "f03b50df7f4a161078fa903c44f37272961b70358d4014d30a12888e1fd2caf1", "TT_test_session_id_and_passphrase.py::test_passphrase_ack_mismatch": "95a40f79fa7ffceb10e89b513c203b4937112b8d764cdba3c1df538355dc129c", -"TT_test_session_id_and_passphrase.py::test_passphrase_always_on_device": "ea3049463b15861f70fab2d4a56fc5efd75b6e6464b9f81e19478e9f8ef48f8e", +"TT_test_session_id_and_passphrase.py::test_passphrase_always_on_device": "d2302e826a90ac2aaec7f7bcf9794fe1ffa077f04d9566ce4edbd53975caee16", "TT_test_session_id_and_passphrase.py::test_passphrase_length": "928b70f715015cb291bcd9890190dc53a4a31a7965d7b7eadfa5ec2e7a3a47a1", "TT_test_session_id_and_passphrase.py::test_passphrase_missing": "95a40f79fa7ffceb10e89b513c203b4937112b8d764cdba3c1df538355dc129c", -"TT_test_session_id_and_passphrase.py::test_passphrase_on_device": "9e9111f802d024580996acb984e520d68b0b61efaec50173f8755770586fd7bd", +"TT_test_session_id_and_passphrase.py::test_passphrase_on_device": "3425b0aa0fb34c5fb40dadd63533701ba93b752b9218d8cce72cc9c9e24c0236", "TT_test_session_id_and_passphrase.py::test_session_enable_passphrase": "9c96dc98e60e7b3a16d7f328f92a001e8c6727902e6912100fc45b3dabee612c", "TT_test_session_id_and_passphrase.py::test_session_with_passphrase": "a7046b2ffe82d6b5194ad34d17d0fa2e16bfa29ab8df798dc38d2b40de01c434", "TT_tezos-test_getaddress.py::test_tezos_get_address": "d5c6a3fcb7bf58d62be0eb6d3d51fa17ebeadfb8ec3359f11e5b3771bb33e4f4",