mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-20 06:28:09 +00:00
feat(core/rust): show WRONG PIN header in PIN entry after bad previous PIN input
[no changelog]
This commit is contained in:
parent
49ce5eb05f
commit
84659dc904
@ -19,6 +19,8 @@ pub struct ChangingTextLine<T> {
|
|||||||
/// What to show in front of the text if it doesn't fit.
|
/// What to show in front of the text if it doesn't fit.
|
||||||
ellipsis: &'static str,
|
ellipsis: &'static str,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
|
/// Whether to show the text completely aligned to the top of the bounds
|
||||||
|
text_at_the_top: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ChangingTextLine<T>
|
impl<T> ChangingTextLine<T>
|
||||||
@ -33,6 +35,7 @@ where
|
|||||||
show_content: true,
|
show_content: true,
|
||||||
ellipsis: "...",
|
ellipsis: "...",
|
||||||
alignment,
|
alignment,
|
||||||
|
text_at_the_top: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +53,12 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Showing text at the very top
|
||||||
|
pub fn with_text_at_the_top(mut self) -> Self {
|
||||||
|
self.text_at_the_top = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the text to be displayed in the line.
|
/// Update the text to be displayed in the line.
|
||||||
pub fn update_text(&mut self, text: T) {
|
pub fn update_text(&mut self, text: T) {
|
||||||
self.text = text;
|
self.text = text;
|
||||||
@ -60,6 +69,11 @@ where
|
|||||||
&self.text
|
&self.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changing the current font
|
||||||
|
pub fn update_font(&mut self, font: Font) {
|
||||||
|
self.font = font;
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether we should display the text content.
|
/// Whether we should display the text content.
|
||||||
/// If not, the whole area (Pad) will still be cleared.
|
/// If not, the whole area (Pad) will still be cleared.
|
||||||
/// Is valid until this function is called again.
|
/// Is valid until this function is called again.
|
||||||
@ -76,7 +90,13 @@ where
|
|||||||
|
|
||||||
/// Y coordinate of text baseline, is the same for all paints.
|
/// Y coordinate of text baseline, is the same for all paints.
|
||||||
fn y_baseline(&self) -> i16 {
|
fn y_baseline(&self) -> i16 {
|
||||||
self.pad.area.y0 + self.font.line_height()
|
let y_coord = self.pad.area.y0 + self.font.line_height();
|
||||||
|
if self.text_at_the_top {
|
||||||
|
// Shifting the text up by 2 pixels.
|
||||||
|
y_coord - 2
|
||||||
|
} else {
|
||||||
|
y_coord
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the whole text can be painted in the available space
|
/// Whether the whole text can be painted in the available space
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
trezorhal::random,
|
trezorhal::random,
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
|
component::{text::common::TextBox, Child, Component, ComponentExt, Event, EventCtx},
|
||||||
display::Icon,
|
display::{Font, Icon},
|
||||||
geometry::Rect,
|
geometry::Rect,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -80,8 +80,12 @@ impl<T: StringType + Clone> ChoiceFactory<T> for ChoiceFactoryPIN {
|
|||||||
/// Component for entering a PIN.
|
/// Component for entering a PIN.
|
||||||
pub struct PinEntry<T: StringType + Clone> {
|
pub struct PinEntry<T: StringType + Clone> {
|
||||||
choice_page: ChoicePage<ChoiceFactoryPIN, T, PinAction>,
|
choice_page: ChoicePage<ChoiceFactoryPIN, T, PinAction>,
|
||||||
|
header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
||||||
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
||||||
|
prompt: T,
|
||||||
subprompt: T,
|
subprompt: T,
|
||||||
|
/// Whether we already show the "real" prompt (not the warning).
|
||||||
|
showing_real_prompt: bool,
|
||||||
show_real_pin: bool,
|
show_real_pin: bool,
|
||||||
show_last_digit: bool,
|
show_last_digit: bool,
|
||||||
textbox: TextBox<MAX_PIN_LENGTH>,
|
textbox: TextBox<MAX_PIN_LENGTH>,
|
||||||
@ -91,22 +95,45 @@ impl<T> PinEntry<T>
|
|||||||
where
|
where
|
||||||
T: StringType + Clone,
|
T: StringType + Clone,
|
||||||
{
|
{
|
||||||
pub fn new(subprompt: T) -> Self {
|
pub fn new(prompt: T, subprompt: T) -> Self {
|
||||||
let pin_line_content = if !subprompt.as_ref().is_empty() {
|
// When subprompt is not empty, it means that the user has entered bad PIN
|
||||||
String::from(subprompt.as_ref())
|
// before. In this case we show the warning together with the subprompt
|
||||||
|
// at the beginning. (WRONG PIN will be replaced by real prompt after
|
||||||
|
// any button click.)
|
||||||
|
let show_subprompt = !subprompt.as_ref().is_empty();
|
||||||
|
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
|
||||||
|
(
|
||||||
|
false,
|
||||||
|
String::from("WRONG PIN"),
|
||||||
|
String::from(subprompt.as_ref()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
String::from(EMPTY_PIN_STR)
|
(
|
||||||
|
true,
|
||||||
|
String::from(prompt.as_ref()),
|
||||||
|
String::from(EMPTY_PIN_STR),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut pin_line = ChangingTextLine::center_bold(pin_line_content).without_ellipsis();
|
||||||
|
if show_subprompt {
|
||||||
|
pin_line.update_font(Font::NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// Starting at a random digit.
|
// Starting at a random digit.
|
||||||
choice_page: ChoicePage::new(ChoiceFactoryPIN)
|
choice_page: ChoicePage::new(ChoiceFactoryPIN)
|
||||||
.with_initial_page_counter(get_random_digit_position())
|
.with_initial_page_counter(get_random_digit_position())
|
||||||
.with_carousel(true),
|
.with_carousel(true),
|
||||||
pin_line: Child::new(
|
header_line: Child::new(
|
||||||
ChangingTextLine::center_bold(pin_line_content).without_ellipsis(),
|
ChangingTextLine::center_bold(header_line_content)
|
||||||
|
.without_ellipsis()
|
||||||
|
.with_text_at_the_top(),
|
||||||
),
|
),
|
||||||
|
pin_line: Child::new(pin_line),
|
||||||
subprompt,
|
subprompt,
|
||||||
|
prompt,
|
||||||
|
showing_real_prompt,
|
||||||
show_real_pin: false,
|
show_real_pin: false,
|
||||||
show_last_digit: false,
|
show_last_digit: false,
|
||||||
textbox: TextBox::empty(),
|
textbox: TextBox::empty(),
|
||||||
@ -122,7 +149,10 @@ where
|
|||||||
/// Show updated content in the changing line.
|
/// Show updated content in the changing line.
|
||||||
/// Many possibilities, according to the PIN state.
|
/// Many possibilities, according to the PIN state.
|
||||||
fn update_pin_line(&mut self, ctx: &mut EventCtx) {
|
fn update_pin_line(&mut self, ctx: &mut EventCtx) {
|
||||||
|
let mut used_font = Font::BOLD;
|
||||||
let pin_line_text = if self.is_empty() && !self.subprompt.as_ref().is_empty() {
|
let pin_line_text = if self.is_empty() && !self.subprompt.as_ref().is_empty() {
|
||||||
|
// Showing the subprompt in NORMAL font
|
||||||
|
used_font = Font::NORMAL;
|
||||||
String::from(self.subprompt.as_ref())
|
String::from(self.subprompt.as_ref())
|
||||||
} else if self.is_empty() {
|
} else if self.is_empty() {
|
||||||
String::from(EMPTY_PIN_STR)
|
String::from(EMPTY_PIN_STR)
|
||||||
@ -144,11 +174,20 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.pin_line.mutate(ctx, |ctx, pin_line| {
|
self.pin_line.mutate(ctx, |ctx, pin_line| {
|
||||||
|
pin_line.update_font(used_font);
|
||||||
pin_line.update_text(pin_line_text);
|
pin_line.update_text(pin_line_text);
|
||||||
pin_line.request_complete_repaint(ctx);
|
pin_line.request_complete_repaint(ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Showing the real prompt instead of WRONG PIN
|
||||||
|
fn show_prompt(&mut self, ctx: &mut EventCtx) {
|
||||||
|
self.header_line.mutate(ctx, |ctx, header_line| {
|
||||||
|
header_line.update_text(String::from(self.prompt.as_ref()));
|
||||||
|
header_line.request_complete_repaint(ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pin(&self) -> &str {
|
pub fn pin(&self) -> &str {
|
||||||
self.textbox.content()
|
self.textbox.content()
|
||||||
}
|
}
|
||||||
@ -169,8 +208,11 @@ where
|
|||||||
type Msg = CancelConfirmMsg;
|
type Msg = CancelConfirmMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
let header_height = self.header_line.inner().needed_height();
|
||||||
|
let (header_area, rest) = bounds.split_top(header_height);
|
||||||
let pin_height = self.pin_line.inner().needed_height();
|
let pin_height = self.pin_line.inner().needed_height();
|
||||||
let (pin_area, choice_area) = bounds.split_top(pin_height);
|
let (pin_area, choice_area) = rest.split_top(pin_height);
|
||||||
|
self.header_line.place(header_area);
|
||||||
self.pin_line.place(pin_area);
|
self.pin_line.place(pin_area);
|
||||||
self.choice_page.place(choice_area);
|
self.choice_page.place(choice_area);
|
||||||
bounds
|
bounds
|
||||||
@ -188,6 +230,14 @@ where
|
|||||||
self.update(ctx)
|
self.update(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Any button event will show the "real" prompt
|
||||||
|
if !self.showing_real_prompt {
|
||||||
|
if let Event::Button(_) = event {
|
||||||
|
self.show_prompt(ctx);
|
||||||
|
self.showing_real_prompt = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match self.choice_page.event(ctx, event) {
|
match self.choice_page.event(ctx, event) {
|
||||||
Some(PinAction::Delete) => {
|
Some(PinAction::Delete) => {
|
||||||
self.textbox.delete_last(ctx);
|
self.textbox.delete_last(ctx);
|
||||||
@ -214,6 +264,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
|
self.header_line.paint();
|
||||||
self.pin_line.paint();
|
self.pin_line.paint();
|
||||||
self.choice_page.paint();
|
self.choice_page.paint();
|
||||||
}
|
}
|
||||||
|
@ -1207,8 +1207,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
|||||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||||
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
|
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
|
||||||
|
|
||||||
let obj =
|
let obj = LayoutObj::new(PinEntry::new(prompt, subprompt))?;
|
||||||
LayoutObj::new(Frame::new(prompt, PinEntry::new(subprompt)).with_title_centered())?;
|
|
||||||
|
|
||||||
Ok(obj.into())
|
Ok(obj.into())
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user