1
0
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:
grdddj 2023-09-19 14:23:29 +02:00 committed by Jiří Musil
parent 29c16c212d
commit e06335d877
16 changed files with 120 additions and 68 deletions

View File

@ -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,
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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 => {

View File

@ -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);

View File

@ -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) {

View File

@ -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),

View File

@ -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));
}
}
};

View File

@ -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) {

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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
}

View File

@ -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() {

View File

@ -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;

View File

@ -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);