mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-30 20:02:34 +00:00
feat(core): create new design for PIN entry
This commit is contained in:
parent
736e6b003a
commit
51fb99d792
@ -13,11 +13,13 @@ use crate::{
|
|||||||
event::TouchEvent,
|
event::TouchEvent,
|
||||||
geometry::{Alignment, Alignment2D, Grid, Insets, Offset, Rect},
|
geometry::{Alignment, Alignment2D, Grid, Insets, Offset, Rect},
|
||||||
model_mercury::component::{
|
model_mercury::component::{
|
||||||
button::{Button, ButtonContent, ButtonMsg, ButtonMsg::Clicked},
|
button::{
|
||||||
|
Button, ButtonContent,
|
||||||
|
ButtonMsg::{self, Clicked},
|
||||||
|
},
|
||||||
theme,
|
theme,
|
||||||
},
|
},
|
||||||
shape,
|
shape::{self, Renderer},
|
||||||
shape::Renderer,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,11 +33,11 @@ const MAX_VISIBLE_DOTS: usize = 14;
|
|||||||
const MAX_VISIBLE_DIGITS: usize = 16;
|
const MAX_VISIBLE_DIGITS: usize = 16;
|
||||||
const DIGIT_COUNT: usize = 10; // 0..10
|
const DIGIT_COUNT: usize = 10; // 0..10
|
||||||
|
|
||||||
const HEADER_PADDING_SIDE: i16 = 5;
|
const HEADER_PADDING_SIDE: i16 = 2;
|
||||||
const HEADER_PADDING_BOTTOM: i16 = 12;
|
const HEADER_PADDING_BOTTOM: i16 = 2;
|
||||||
|
|
||||||
const HEADER_PADDING: Insets = Insets::new(
|
const HEADER_PADDING: Insets = Insets::new(
|
||||||
theme::borders().top,
|
0,
|
||||||
HEADER_PADDING_SIDE,
|
HEADER_PADDING_SIDE,
|
||||||
HEADER_PADDING_BOTTOM,
|
HEADER_PADDING_BOTTOM,
|
||||||
HEADER_PADDING_SIDE,
|
HEADER_PADDING_SIDE,
|
||||||
@ -60,8 +62,8 @@ where
|
|||||||
T: AsRef<str>,
|
T: AsRef<str>,
|
||||||
{
|
{
|
||||||
// Label position fine-tuning.
|
// Label position fine-tuning.
|
||||||
const MAJOR_OFF: Offset = Offset::y(11);
|
const MAJOR_OFF: Offset = Offset::y(0);
|
||||||
const MINOR_OFF: Offset = Offset::y(11);
|
const MINOR_OFF: Offset = Offset::y(0);
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
major_prompt: T,
|
major_prompt: T,
|
||||||
@ -70,17 +72,13 @@ where
|
|||||||
allow_cancel: bool,
|
allow_cancel: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Control buttons.
|
// Control buttons.
|
||||||
let erase_btn = Button::with_icon_blend(
|
let erase_btn = Button::with_icon(theme::ICON_DELETE)
|
||||||
theme::IMAGE_BG_BACK_BTN,
|
.styled(theme::button_pin_erase())
|
||||||
theme::ICON_BACK,
|
.with_long_press(theme::ERASE_HOLD_DURATION)
|
||||||
Offset::new(30, 12),
|
.initially_enabled(false);
|
||||||
)
|
|
||||||
.styled(theme::button_reset())
|
|
||||||
.with_long_press(theme::ERASE_HOLD_DURATION)
|
|
||||||
.initially_enabled(false);
|
|
||||||
let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child();
|
let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child();
|
||||||
|
|
||||||
let cancel_btn = Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel());
|
let cancel_btn = Button::with_icon(theme::ICON_CANCEL).styled(theme::button_pin_cancel());
|
||||||
let cancel_btn = Maybe::new(theme::BG, cancel_btn, allow_cancel).into_child();
|
let cancel_btn = Maybe::new(theme::BG, cancel_btn, allow_cancel).into_child();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -96,7 +94,7 @@ where
|
|||||||
erase_btn,
|
erase_btn,
|
||||||
cancel_btn,
|
cancel_btn,
|
||||||
confirm_btn: Button::with_icon(theme::ICON_CONFIRM)
|
confirm_btn: Button::with_icon(theme::ICON_CONFIRM)
|
||||||
.styled(theme::button_confirm())
|
.styled(theme::button_pin_confirm())
|
||||||
.initially_enabled(false)
|
.initially_enabled(false)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
digit_btns: Self::generate_digit_buttons(),
|
digit_btns: Self::generate_digit_buttons(),
|
||||||
@ -155,23 +153,16 @@ where
|
|||||||
type Msg = PinKeyboardMsg;
|
type Msg = PinKeyboardMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
// Ignore the top padding for now, we need it to reliably register textbox touch
|
|
||||||
// events.
|
|
||||||
let borders_no_top = Insets {
|
|
||||||
top: 0,
|
|
||||||
..theme::borders()
|
|
||||||
};
|
|
||||||
// Prompts and PIN dots display.
|
// Prompts and PIN dots display.
|
||||||
let (header, keypad) = bounds
|
let (header, keypad) =
|
||||||
.inset(borders_no_top)
|
bounds.split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::PIN_BUTTON_SPACING);
|
||||||
.split_bottom(4 * theme::PIN_BUTTON_HEIGHT + 3 * theme::BUTTON_SPACING);
|
|
||||||
let prompt = header.inset(HEADER_PADDING);
|
let prompt = header.inset(HEADER_PADDING);
|
||||||
// the inset -3 is a workaround for long text in "re-enter wipe code"
|
// 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 major_area = prompt.translate(Self::MAJOR_OFF).inset(Insets::right(-3));
|
||||||
let minor_area = prompt.translate(Self::MINOR_OFF);
|
let minor_area = prompt.translate(Self::MINOR_OFF);
|
||||||
|
|
||||||
// Control buttons.
|
// Control buttons.
|
||||||
let grid = Grid::new(keypad, 4, 3).with_spacing(theme::BUTTON_SPACING);
|
let grid = Grid::new(keypad, 4, 3).with_spacing(theme::PIN_BUTTON_SPACING);
|
||||||
|
|
||||||
// Prompts and PIN dots display.
|
// Prompts and PIN dots display.
|
||||||
self.textbox_pad.place(header);
|
self.textbox_pad.place(header);
|
||||||
@ -272,6 +263,7 @@ where
|
|||||||
fn render(&mut self, target: &mut impl Renderer) {
|
fn render(&mut self, target: &mut impl Renderer) {
|
||||||
self.erase_btn.render(target);
|
self.erase_btn.render(target);
|
||||||
self.textbox_pad.render(target);
|
self.textbox_pad.render(target);
|
||||||
|
|
||||||
if self.textbox.inner().is_empty() {
|
if self.textbox.inner().is_empty() {
|
||||||
if let Some(ref mut w) = self.major_warning {
|
if let Some(ref mut w) = self.major_warning {
|
||||||
w.render(target);
|
w.render(target);
|
||||||
@ -283,7 +275,12 @@ where
|
|||||||
} else {
|
} else {
|
||||||
self.textbox.render(target);
|
self.textbox.render(target);
|
||||||
}
|
}
|
||||||
self.confirm_btn.render(target);
|
|
||||||
|
// Painting the confirm only if there is already a pin
|
||||||
|
if !self.textbox.inner().is_empty() {
|
||||||
|
self.confirm_btn.render(target);
|
||||||
|
}
|
||||||
|
|
||||||
for btn in &mut self.digit_btns {
|
for btn in &mut self.digit_btns {
|
||||||
btn.render(target);
|
btn.render(target);
|
||||||
}
|
}
|
||||||
@ -390,13 +387,14 @@ impl PinDots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_digits(&self, area: Rect, target: &mut impl Renderer) {
|
fn render_digits(&self, area: Rect, target: &mut impl Renderer) {
|
||||||
|
let left = area.left_center() + Offset::y(Font::MONO.text_height() / 2);
|
||||||
let center = area.center() + Offset::y(Font::MONO.text_height() / 2);
|
let center = area.center() + Offset::y(Font::MONO.text_height() / 2);
|
||||||
let right = center + Offset::x(Font::MONO.text_width("0") * (MAX_VISIBLE_DOTS as i16) / 2);
|
let right = center + Offset::x(Font::MONO.text_width("0") * (MAX_VISIBLE_DOTS as i16) / 2);
|
||||||
let digits = self.digits.len();
|
let digits = self.digits.len();
|
||||||
|
|
||||||
if digits <= MAX_VISIBLE_DOTS {
|
if digits <= MAX_VISIBLE_DOTS {
|
||||||
shape::Text::new(center, &self.digits)
|
shape::Text::new(left, &self.digits)
|
||||||
.with_align(Alignment::Center)
|
.with_align(Alignment::Start)
|
||||||
.with_font(Font::MONO)
|
.with_font(Font::MONO)
|
||||||
.with_fg(self.style.text_color)
|
.with_fg(self.style.text_color)
|
||||||
.render(target);
|
.render(target);
|
||||||
@ -456,7 +454,7 @@ impl PinDots {
|
|||||||
|
|
||||||
fn render_dots(&self, area: Rect, target: &mut impl Renderer) {
|
fn render_dots(&self, area: Rect, target: &mut impl Renderer) {
|
||||||
// let mut cursor = self.size().snap(area.left_center(), Alignment2D::CENTER);
|
// let mut cursor = self.size().snap(area.left_center(), Alignment2D::CENTER);
|
||||||
let cursor = area.bottom_left();
|
let mut cursor = area.bottom_left();
|
||||||
|
|
||||||
let digits = self.digits.len();
|
let digits = self.digits.len();
|
||||||
let dots_visible = digits.min(MAX_VISIBLE_DOTS);
|
let dots_visible = digits.min(MAX_VISIBLE_DOTS);
|
||||||
|
@ -43,6 +43,7 @@ pub const GREY_LIGHT: Color = Color::rgb(0x90, 0x90, 0x90); // secondary text
|
|||||||
pub const GREY_MEDIUM: Color = Color::rgb(0x4F, 0x4F, 0x4F); // button pressed
|
pub const GREY_MEDIUM: Color = Color::rgb(0x4F, 0x4F, 0x4F); // button pressed
|
||||||
pub const GREY_DARK: Color = Color::rgb(0x35, 0x35, 0x35); // button
|
pub const GREY_DARK: Color = Color::rgb(0x35, 0x35, 0x35); // button
|
||||||
pub const VIOLET: Color = Color::rgb(0x95, 0x00, 0xCA);
|
pub const VIOLET: Color = Color::rgb(0x95, 0x00, 0xCA);
|
||||||
|
pub const PIN_BUTTON_COLOR: Color = Color::rgb(0x16, 0x1F, 0x24);
|
||||||
|
|
||||||
pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
||||||
pub const FATAL_ERROR_HIGHLIGHT_COLOR: Color = Color::rgb(0xFF, 0x41, 0x41);
|
pub const FATAL_ERROR_HIGHLIGHT_COLOR: Color = Color::rgb(0xFF, 0x41, 0x41);
|
||||||
@ -373,10 +374,10 @@ pub const fn button_pin() -> ButtonStyleSheet {
|
|||||||
normal: &ButtonStyle {
|
normal: &ButtonStyle {
|
||||||
font: Font::MONO,
|
font: Font::MONO,
|
||||||
text_color: FG,
|
text_color: FG,
|
||||||
button_color: GREY_DARK,
|
button_color: PIN_BUTTON_COLOR,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
active: &ButtonStyle {
|
active: &ButtonStyle {
|
||||||
@ -385,7 +386,7 @@ pub const fn button_pin() -> ButtonStyleSheet {
|
|||||||
button_color: GREY_MEDIUM,
|
button_color: GREY_MEDIUM,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: FG,
|
border_color: FG,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
disabled: &ButtonStyle {
|
disabled: &ButtonStyle {
|
||||||
@ -394,7 +395,71 @@ pub const fn button_pin() -> ButtonStyleSheet {
|
|||||||
button_color: BG, // so there is no "button" itself, just the text
|
button_color: BG, // so there is no "button" itself, just the text
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn button_pin_cancel() -> ButtonStyleSheet {
|
||||||
|
ButtonStyleSheet {
|
||||||
|
normal: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: RED,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: BG,
|
||||||
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
active: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: RED,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: FG,
|
||||||
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
disabled: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: GREY_LIGHT,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: BG,
|
||||||
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn button_pin_erase() -> ButtonStyleSheet {
|
||||||
|
ButtonStyleSheet {
|
||||||
|
normal: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: GREY_LIGHT,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: BG,
|
||||||
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
active: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: FG,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: FG,
|
||||||
|
border_radius: 0,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
disabled: &ButtonStyle {
|
||||||
|
font: Font::BOLD,
|
||||||
|
text_color: FG,
|
||||||
|
button_color: BG,
|
||||||
|
background_color: BG,
|
||||||
|
border_color: BG,
|
||||||
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -403,30 +468,30 @@ pub const fn button_pin() -> ButtonStyleSheet {
|
|||||||
pub const fn button_pin_confirm() -> ButtonStyleSheet {
|
pub const fn button_pin_confirm() -> ButtonStyleSheet {
|
||||||
ButtonStyleSheet {
|
ButtonStyleSheet {
|
||||||
normal: &ButtonStyle {
|
normal: &ButtonStyle {
|
||||||
font: Font::MONO,
|
font: Font::BOLD,
|
||||||
text_color: FG,
|
text_color: GREEN,
|
||||||
button_color: GREEN,
|
button_color: BG,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
active: &ButtonStyle {
|
active: &ButtonStyle {
|
||||||
font: Font::MONO,
|
font: Font::BOLD,
|
||||||
text_color: FG,
|
text_color: GREEN,
|
||||||
button_color: GREEN_DARK,
|
button_color: BG,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: FG,
|
border_color: GREEN,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
disabled: &ButtonStyle {
|
disabled: &ButtonStyle {
|
||||||
font: Font::MONO,
|
font: Font::BOLD,
|
||||||
text_color: GREY_LIGHT,
|
text_color: GREY_LIGHT,
|
||||||
button_color: GREY_DARK,
|
button_color: BG,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
border_color: BG,
|
border_color: BG,
|
||||||
border_radius: RADIUS,
|
border_radius: 0,
|
||||||
border_width: 0,
|
border_width: 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -657,7 +722,8 @@ pub const RECOVERY_SPACING: i16 = 18;
|
|||||||
pub const CORNER_BUTTON_SIDE: i16 = 44;
|
pub const CORNER_BUTTON_SIDE: i16 = 44;
|
||||||
pub const CORNER_BUTTON_SPACING: i16 = BUTTON_SPACING;
|
pub const CORNER_BUTTON_SPACING: i16 = BUTTON_SPACING;
|
||||||
pub const INFO_BUTTON_HEIGHT: i16 = 44;
|
pub const INFO_BUTTON_HEIGHT: i16 = 44;
|
||||||
pub const PIN_BUTTON_HEIGHT: i16 = 40;
|
pub const PIN_BUTTON_HEIGHT: i16 = 52;
|
||||||
|
pub const PIN_BUTTON_SPACING: i16 = 2;
|
||||||
pub const MNEMONIC_BUTTON_HEIGHT: i16 = 52;
|
pub const MNEMONIC_BUTTON_HEIGHT: i16 = 52;
|
||||||
pub const RESULT_PADDING: i16 = 6;
|
pub const RESULT_PADDING: i16 = 6;
|
||||||
pub const RESULT_FOOTER_START: i16 = 171;
|
pub const RESULT_FOOTER_START: i16 = 171;
|
||||||
|
@ -180,10 +180,11 @@ impl Loader {
|
|||||||
let text_width = style.font.text_width(t);
|
let text_width = style.font.text_width(t);
|
||||||
let text_height = style.font.text_max_height();
|
let text_height = style.font.text_max_height();
|
||||||
|
|
||||||
let pt = self.area.top_left() + Offset::new(
|
let pt = self.area.top_left()
|
||||||
(self.area.width() - text_width) / 2,
|
+ Offset::new(
|
||||||
(self.area.height() - text_height) / 2,
|
(self.area.width() - text_width) / 2,
|
||||||
);
|
(self.area.height() - text_height) / 2,
|
||||||
|
);
|
||||||
|
|
||||||
shape::Text::new(pt, t)
|
shape::Text::new(pt, t)
|
||||||
.with_baseline(false)
|
.with_baseline(false)
|
||||||
|
Loading…
Reference in New Issue
Block a user