mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-02 10:51:06 +00:00
feat(core/rust): differentiate between short and long button press
With the help of this, making long DELETE press delete all the inputted characters in PIN or passphrase entries. [no changelog]
This commit is contained in:
parent
29c16c212d
commit
e06335d877
@ -155,7 +155,7 @@ impl<'a> Component for Confirm<'a> {
|
||||
let msg = self.buttons.event(ctx, event);
|
||||
if self.showing_info_screen {
|
||||
// Showing the info screen currently - going back with the left button
|
||||
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) = msg {
|
||||
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Left, _)) = msg {
|
||||
self.showing_info_screen = false;
|
||||
self.update_everything(ctx);
|
||||
};
|
||||
@ -163,11 +163,13 @@ impl<'a> Component for Confirm<'a> {
|
||||
} else if self.has_info_screen() {
|
||||
// Being on the "main" screen but with an info screen available on the right
|
||||
match msg {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left, _)) => {
|
||||
Some(ConfirmMsg::Cancel)
|
||||
}
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle, _)) => {
|
||||
Some(ConfirmMsg::Confirm)
|
||||
}
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right, _)) => {
|
||||
self.showing_info_screen = true;
|
||||
self.update_everything(ctx);
|
||||
None
|
||||
@ -176,8 +178,10 @@ impl<'a> Component for Confirm<'a> {
|
||||
}
|
||||
} else if self.two_btn_confirm {
|
||||
match msg {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left, _)) => {
|
||||
Some(ConfirmMsg::Cancel)
|
||||
}
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Middle, _)) => {
|
||||
Some(ConfirmMsg::Confirm)
|
||||
}
|
||||
_ => None,
|
||||
@ -185,8 +189,12 @@ impl<'a> Component for Confirm<'a> {
|
||||
} else {
|
||||
// There is just one main screen without info screen
|
||||
match msg {
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) => Some(ConfirmMsg::Confirm),
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Left, _)) => {
|
||||
Some(ConfirmMsg::Cancel)
|
||||
}
|
||||
Some(ButtonControllerMsg::Triggered(ButtonPos::Right, _)) => {
|
||||
Some(ConfirmMsg::Confirm)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -67,10 +67,10 @@ impl<'a> Component for Intro<'a> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let msg = self.buttons.event(ctx, event);
|
||||
|
||||
if let Some(Triggered(ButtonPos::Left)) = msg {
|
||||
if let Some(Triggered(ButtonPos::Left, _)) = msg {
|
||||
return Some(Self::Msg::InstallFirmware);
|
||||
};
|
||||
if let Some(Triggered(ButtonPos::Right)) = msg {
|
||||
if let Some(Triggered(ButtonPos::Right, _)) = msg {
|
||||
return Some(Self::Msg::GoToMenu);
|
||||
};
|
||||
None
|
||||
|
@ -141,7 +141,7 @@ impl Component for Menu {
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.choice_page.event(ctx, event)
|
||||
self.choice_page.event(ctx, event).map(|evt| evt.0)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
|
@ -204,7 +204,7 @@ where
|
||||
};
|
||||
|
||||
let button_event = self.buttons.event(ctx, event);
|
||||
if let Some(ButtonControllerMsg::Triggered(button)) = button_event {
|
||||
if let Some(ButtonControllerMsg::Triggered(button, _)) = button_event {
|
||||
if self.is_in_subpage() {
|
||||
match button {
|
||||
ButtonPos::Left => {
|
||||
|
@ -3,7 +3,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
time::Duration,
|
||||
time::{Duration, Instant},
|
||||
ui::{
|
||||
component::{base::Event, Component, EventCtx, Pad, TimerToken},
|
||||
event::{ButtonEvent, PhysicalButton},
|
||||
@ -40,7 +40,8 @@ enum ButtonState {
|
||||
|
||||
pub enum ButtonControllerMsg {
|
||||
Pressed(ButtonPos),
|
||||
Triggered(ButtonPos),
|
||||
/// Which button was triggered, and whether it was pressed for a longer time
|
||||
Triggered(ButtonPos, bool),
|
||||
}
|
||||
|
||||
/// Defines what kind of button should be currently used.
|
||||
@ -105,6 +106,11 @@ where
|
||||
{
|
||||
pos: ButtonPos,
|
||||
button_type: ButtonType<T>,
|
||||
/// Holds the timestamp of when the button was pressed.
|
||||
pressed_since: Option<Instant>,
|
||||
/// How long the button should be pressed to send `long_press=true` in
|
||||
/// `Triggered`
|
||||
long_press_ms: u32,
|
||||
}
|
||||
|
||||
impl<T> ButtonContainer<T>
|
||||
@ -114,9 +120,12 @@ where
|
||||
/// Supplying `None` as `btn_details` marks the button inactive
|
||||
/// (it can be later activated in `set()`).
|
||||
pub fn new(pos: ButtonPos, btn_details: Option<ButtonDetails<T>>) -> Self {
|
||||
const DEFAULT_LONG_PRESS_MS: u32 = 1000;
|
||||
Self {
|
||||
pos,
|
||||
button_type: ButtonType::from_button_details(pos, btn_details),
|
||||
pressed_since: None,
|
||||
long_press_ms: DEFAULT_LONG_PRESS_MS,
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +160,14 @@ where
|
||||
/// hold.
|
||||
pub fn maybe_trigger(&mut self, ctx: &mut EventCtx) -> Option<ButtonControllerMsg> {
|
||||
match self.button_type {
|
||||
ButtonType::Button(_) => Some(ButtonControllerMsg::Triggered(self.pos)),
|
||||
ButtonType::Button(_) => {
|
||||
// Finding out whether the button was long-pressed
|
||||
let long_press = self.pressed_since.map_or(false, |since| {
|
||||
Instant::now().saturating_duration_since(since).to_millis() > self.long_press_ms
|
||||
});
|
||||
self.pressed_since = None;
|
||||
Some(ButtonControllerMsg::Triggered(self.pos, long_press))
|
||||
}
|
||||
_ => {
|
||||
self.hold_ended(ctx);
|
||||
None
|
||||
@ -169,6 +185,11 @@ where
|
||||
false
|
||||
}
|
||||
|
||||
/// Saving the timestamp of when the button was pressed.
|
||||
pub fn got_pressed(&mut self) {
|
||||
self.pressed_since = Some(Instant::now());
|
||||
}
|
||||
|
||||
/// Registering hold event.
|
||||
pub fn hold_started(&mut self, ctx: &mut EventCtx) {
|
||||
if let ButtonType::HoldToConfirm(htc) = &mut self.button_type {
|
||||
@ -283,15 +304,15 @@ where
|
||||
if self.left_btn.htc_got_triggered(ctx, event) {
|
||||
self.state = ButtonState::HTCNeedsRelease(PhysicalButton::Left);
|
||||
self.set_pressed(ctx, false, false, false);
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Left));
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Left, true));
|
||||
} else if self.middle_btn.htc_got_triggered(ctx, event) {
|
||||
self.state = ButtonState::Nothing;
|
||||
self.set_pressed(ctx, false, false, false);
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Middle));
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Middle, true));
|
||||
} else if self.right_btn.htc_got_triggered(ctx, event) {
|
||||
self.state = ButtonState::HTCNeedsRelease(PhysicalButton::Right);
|
||||
self.set_pressed(ctx, false, false, false);
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Right));
|
||||
return Some(ButtonControllerMsg::Triggered(ButtonPos::Right, true));
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -325,11 +346,13 @@ where
|
||||
match which {
|
||||
// ▼ *
|
||||
PhysicalButton::Left => {
|
||||
self.left_btn.got_pressed();
|
||||
self.left_btn.hold_started(ctx);
|
||||
Some(ButtonControllerMsg::Pressed(ButtonPos::Left))
|
||||
}
|
||||
// * ▼
|
||||
PhysicalButton::Right => {
|
||||
self.right_btn.got_pressed();
|
||||
self.right_btn.hold_started(ctx);
|
||||
Some(ButtonControllerMsg::Pressed(ButtonPos::Right))
|
||||
}
|
||||
@ -369,6 +392,7 @@ where
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.middle_btn.got_pressed();
|
||||
self.middle_hold_started(ctx);
|
||||
(
|
||||
// ↓ ↓
|
||||
@ -399,7 +423,7 @@ where
|
||||
// ▲ * | * ▲
|
||||
ButtonEvent::ButtonReleased(b) if b != which_up => {
|
||||
// _ _
|
||||
// Both button needs to be clickable now
|
||||
// Both buttons need to be clickable now
|
||||
if let Some(ignore_btn_delay) = &mut self.ignore_btn_delay {
|
||||
ignore_btn_delay.make_button_clickable(ButtonPos::Left);
|
||||
ignore_btn_delay.make_button_clickable(ButtonPos::Right);
|
||||
|
@ -256,7 +256,7 @@ where
|
||||
|
||||
// Do something when a button was triggered
|
||||
// and we have some action connected with it
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = button_event {
|
||||
if let Some(ButtonControllerMsg::Triggered(pos, _)) = button_event {
|
||||
// When there is a previous or next screen in the current flow,
|
||||
// handle that first and in case it triggers, then do not continue
|
||||
if self.event_consumed_by_current_choice(ctx, pos) {
|
||||
|
@ -144,7 +144,7 @@ where
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
Self::event_usb(self, ctx, event);
|
||||
// HTC press of any button will lock the device
|
||||
if let Some(ButtonControllerMsg::Triggered(_)) = self.invisible_buttons.event(ctx, event) {
|
||||
if let Some(ButtonControllerMsg::Triggered(..)) = self.invisible_buttons.event(ctx, event) {
|
||||
return Some(());
|
||||
}
|
||||
None
|
||||
@ -202,7 +202,7 @@ where
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
// Press of any button will unlock the device
|
||||
if let Some(ButtonControllerMsg::Triggered(_)) = self.invisible_buttons.event(ctx, event) {
|
||||
if let Some(ButtonControllerMsg::Triggered(..)) = self.invisible_buttons.event(ctx, event) {
|
||||
return Some(());
|
||||
}
|
||||
None
|
||||
@ -261,7 +261,7 @@ where
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
// Left button cancels, right confirms
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = self.buttons.event(ctx, event) {
|
||||
if let Some(ButtonControllerMsg::Triggered(pos, _)) = self.buttons.event(ctx, event) {
|
||||
match pos {
|
||||
ButtonPos::Left => return Some(CancelConfirmMsg::Cancelled),
|
||||
ButtonPos::Right => return Some(CancelConfirmMsg::Confirmed),
|
||||
|
@ -474,7 +474,7 @@ where
|
||||
F: ChoiceFactory<T, Action = A>,
|
||||
T: StringType + Clone,
|
||||
{
|
||||
type Msg = A;
|
||||
type Msg = (A, bool);
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let (content_area, button_area) = bounds.split_bottom(theme::BUTTON_HEIGHT);
|
||||
@ -516,7 +516,7 @@ where
|
||||
// Stopping the automatic movement when the released button is the same as the
|
||||
// direction we were moving, or when the pressed button is the
|
||||
// opposite one (user does middle-click).
|
||||
if matches!(button_event, Some(ButtonControllerMsg::Triggered(pos)) if pos == moving_direction)
|
||||
if matches!(button_event, Some(ButtonControllerMsg::Triggered(pos, _)) if pos == moving_direction)
|
||||
|| matches!(button_event, Some(ButtonControllerMsg::Pressed(pos)) if pos != moving_direction)
|
||||
{
|
||||
self.holding_mover.stop_moving();
|
||||
@ -534,7 +534,7 @@ where
|
||||
}
|
||||
|
||||
// There was a legitimate button event - doing some action
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = button_event {
|
||||
if let Some(ButtonControllerMsg::Triggered(pos, long_press)) = button_event {
|
||||
match pos {
|
||||
ButtonPos::Left => {
|
||||
// Clicked BACK. Decrease the page counter.
|
||||
@ -547,9 +547,9 @@ where
|
||||
self.move_right(ctx);
|
||||
}
|
||||
ButtonPos::Middle => {
|
||||
// Clicked SELECT. Send current choice index
|
||||
// Clicked SELECT. Send current choice index with information about long-press
|
||||
self.clear_and_repaint(ctx);
|
||||
return Some(self.get_current_choice().1);
|
||||
return Some((self.get_current_choice().1, long_press));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -78,7 +78,7 @@ where
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.choice_page.event(ctx, event)
|
||||
self.choice_page.event(ctx, event).map(|evt| evt.0)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
|
@ -291,6 +291,10 @@ where
|
||||
self.textbox.delete_last(ctx);
|
||||
}
|
||||
|
||||
fn delete_all_digits(&mut self, ctx: &mut EventCtx) {
|
||||
self.textbox.clear(ctx);
|
||||
}
|
||||
|
||||
/// Displaying the MENU
|
||||
fn show_menu_page(&mut self, ctx: &mut EventCtx) {
|
||||
let menu_choices = ChoiceFactoryPassphrase::new(ChoiceCategory::Menu, self.is_empty());
|
||||
@ -359,13 +363,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(action) = self.choice_page.event(ctx, event) {
|
||||
if let Some((action, long_press)) = self.choice_page.event(ctx, event) {
|
||||
match action {
|
||||
PassphraseAction::CancelOrDelete => {
|
||||
if self.is_empty() {
|
||||
return Some(CancelConfirmMsg::Cancelled);
|
||||
} else {
|
||||
self.delete_last_digit(ctx);
|
||||
// Deleting all when long-pressed
|
||||
if long_press {
|
||||
self.delete_all_digits(ctx);
|
||||
} else {
|
||||
self.delete_last_digit(ctx);
|
||||
}
|
||||
self.update_passphrase_dots(ctx);
|
||||
if self.is_empty() {
|
||||
// Allowing for DELETE/CANCEL change
|
||||
|
@ -240,29 +240,34 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
match self.choice_page.event(ctx, event) {
|
||||
Some(PinAction::Delete) => {
|
||||
self.textbox.delete_last(ctx);
|
||||
self.update(ctx);
|
||||
None
|
||||
if let Some((action, long_press)) = self.choice_page.event(ctx, event) {
|
||||
match action {
|
||||
PinAction::Delete => {
|
||||
// Deleting all when long-pressed
|
||||
if long_press {
|
||||
self.textbox.clear(ctx);
|
||||
} else {
|
||||
self.textbox.delete_last(ctx);
|
||||
}
|
||||
self.update(ctx);
|
||||
}
|
||||
PinAction::Show => {
|
||||
self.show_real_pin = true;
|
||||
self.update(ctx);
|
||||
}
|
||||
PinAction::Enter => return Some(CancelConfirmMsg::Confirmed),
|
||||
PinAction::Digit(ch) if !self.is_full() => {
|
||||
self.textbox.append(ctx, ch);
|
||||
// Choosing random digit to be shown next
|
||||
self.choice_page
|
||||
.set_page_counter(ctx, get_random_digit_position(), true);
|
||||
self.show_last_digit = true;
|
||||
self.update(ctx);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(PinAction::Show) => {
|
||||
self.show_real_pin = true;
|
||||
self.update(ctx);
|
||||
None
|
||||
}
|
||||
Some(PinAction::Enter) => Some(CancelConfirmMsg::Confirmed),
|
||||
Some(PinAction::Digit(ch)) if !self.is_full() => {
|
||||
self.textbox.append(ctx, ch);
|
||||
// Choosing random digit to be shown next
|
||||
self.choice_page
|
||||
.set_page_counter(ctx, get_random_digit_position(), true);
|
||||
self.show_last_digit = true;
|
||||
self.update(ctx);
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
|
@ -112,7 +112,7 @@ where
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
self.choice_page.event(ctx, event)
|
||||
self.choice_page.event(ctx, event).map(|evt| evt.0)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
|
@ -201,19 +201,25 @@ where
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
match self.choice_page.event(ctx, event) {
|
||||
Some(WordlistAction::Delete) => {
|
||||
self.textbox.delete_last(ctx);
|
||||
self.update(ctx);
|
||||
if let Some((action, long_press)) = self.choice_page.event(ctx, event) {
|
||||
match action {
|
||||
WordlistAction::Delete => {
|
||||
// Deleting all when long-pressed
|
||||
if long_press {
|
||||
self.textbox.clear(ctx);
|
||||
} else {
|
||||
self.textbox.delete_last(ctx);
|
||||
}
|
||||
self.update(ctx);
|
||||
}
|
||||
WordlistAction::Letter(letter) => {
|
||||
self.textbox.append(ctx, letter);
|
||||
self.update(ctx);
|
||||
}
|
||||
WordlistAction::Word(word) => {
|
||||
return Some(word);
|
||||
}
|
||||
}
|
||||
Some(WordlistAction::Letter(letter)) => {
|
||||
self.textbox.append(ctx, letter);
|
||||
self.update(ctx);
|
||||
}
|
||||
Some(WordlistAction::Word(word)) => {
|
||||
return Some(word);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ where
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
ctx.set_page_count(self.page_count());
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = self.buttons.event(ctx, event) {
|
||||
if let Some(ButtonControllerMsg::Triggered(pos, _)) = self.buttons.event(ctx, event) {
|
||||
match pos {
|
||||
ButtonPos::Left => {
|
||||
if self.has_previous_page() {
|
||||
|
@ -134,7 +134,7 @@ where
|
||||
self.text.event(ctx, event);
|
||||
self.headline.event(ctx, event);
|
||||
|
||||
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Right)) =
|
||||
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Right, _)) =
|
||||
self.buttons.event(ctx, event)
|
||||
{
|
||||
button_confirmed = true;
|
||||
|
@ -58,7 +58,7 @@ where
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let button_event = self.buttons.event(ctx, event);
|
||||
|
||||
if let Some(ButtonControllerMsg::Triggered(pos)) = button_event {
|
||||
if let Some(ButtonControllerMsg::Triggered(pos, _)) = button_event {
|
||||
match pos {
|
||||
ButtonPos::Left => {
|
||||
return Some(CancelInfoConfirmMsg::Cancelled);
|
||||
|
Loading…
Reference in New Issue
Block a user