mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-24 15:28:10 +00:00
feat(core/rust): special shape erase button
[no changelog]
This commit is contained in:
parent
f1a3f289bf
commit
38548f02f2
BIN
core/assets/back_btn.png
Normal file
BIN
core/assets/back_btn.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
BIN
core/assets/back_btn_tall.png
Normal file
BIN
core/assets/back_btn_tall.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
@ -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 {
|
||||
|
@ -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<Rect>,
|
||||
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(
|
||||
|
@ -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},
|
||||
|
@ -52,6 +52,10 @@ impl<T> Button<T> {
|
||||
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<T> Button<T> {
|
||||
}
|
||||
|
||||
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<T> Button<T> {
|
||||
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<T> {
|
||||
Empty,
|
||||
Text(T),
|
||||
Icon(&'static [u8]),
|
||||
IconBlend(&'static [u8], &'static [u8], Offset),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
BIN
core/embed/rust/src/ui/model_tt/res/back_btn.toif
Normal file
BIN
core/embed/rust/src/ui/model_tt/res/back_btn.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tt/res/back_btn_tall.toif
Normal file
BIN
core/embed/rust/src/ui/model_tt/res/back_btn_tall.toif
Normal file
Binary file not shown.
@ -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,
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user