diff --git a/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs b/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs index 6b8aa10cdd..40b26ede1c 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs @@ -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, } } diff --git a/core/embed/rust/src/ui/model_tr/bootloader/intro.rs b/core/embed/rust/src/ui/model_tr/bootloader/intro.rs index b40aee70ae..3409280071 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/intro.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/intro.rs @@ -67,10 +67,10 @@ impl<'a> Component for Intro<'a> { fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { 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 diff --git a/core/embed/rust/src/ui/model_tr/bootloader/menu.rs b/core/embed/rust/src/ui/model_tr/bootloader/menu.rs index 488b6da64d..843018cd58 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/menu.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/menu.rs @@ -141,7 +141,7 @@ impl Component for Menu { } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - self.choice_page.event(ctx, event) + self.choice_page.event(ctx, event).map(|evt| evt.0) } fn paint(&mut self) { diff --git a/core/embed/rust/src/ui/model_tr/component/address_details.rs b/core/embed/rust/src/ui/model_tr/component/address_details.rs index c4b47a246e..60e992d568 100644 --- a/core/embed/rust/src/ui/model_tr/component/address_details.rs +++ b/core/embed/rust/src/ui/model_tr/component/address_details.rs @@ -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 => { diff --git a/core/embed/rust/src/ui/model_tr/component/button_controller.rs b/core/embed/rust/src/ui/model_tr/component/button_controller.rs index 618b4e6742..b51296a34a 100644 --- a/core/embed/rust/src/ui/model_tr/component/button_controller.rs +++ b/core/embed/rust/src/ui/model_tr/component/button_controller.rs @@ -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, + /// Holds the timestamp of when the button was pressed. + pressed_since: Option, + /// How long the button should be pressed to send `long_press=true` in + /// `Triggered` + long_press_ms: u32, } impl ButtonContainer @@ -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>) -> 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 { 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); diff --git a/core/embed/rust/src/ui/model_tr/component/flow.rs b/core/embed/rust/src/ui/model_tr/component/flow.rs index 25dad78594..22cb0867ba 100644 --- a/core/embed/rust/src/ui/model_tr/component/flow.rs +++ b/core/embed/rust/src/ui/model_tr/component/flow.rs @@ -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) { diff --git a/core/embed/rust/src/ui/model_tr/component/homescreen.rs b/core/embed/rust/src/ui/model_tr/component/homescreen.rs index 687350c4bd..09a7cd07ef 100644 --- a/core/embed/rust/src/ui/model_tr/component/homescreen.rs +++ b/core/embed/rust/src/ui/model_tr/component/homescreen.rs @@ -144,7 +144,7 @@ where fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { 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 { // 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 { // 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), diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs index 21d45d752a..ae68b40973 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/choice.rs @@ -474,7 +474,7 @@ where F: ChoiceFactory, 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)); } } }; diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs index 1813fe811b..bdc132f0b8 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/number_input.rs @@ -78,7 +78,7 @@ where } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - self.choice_page.event(ctx, event) + self.choice_page.event(ctx, event).map(|evt| evt.0) } fn paint(&mut self) { diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs index c13e480366..6012034d0d 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/passphrase.rs @@ -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 diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs index ef3495631f..670cd5fa29 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/pin.rs @@ -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) { diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs index ca9dc13928..33d99fd9b1 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/simple_choice.rs @@ -112,7 +112,7 @@ where } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - self.choice_page.event(ctx, event) + self.choice_page.event(ctx, event).map(|evt| evt.0) } fn paint(&mut self) { diff --git a/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs b/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs index 637bf364fd..07b582adbb 100644 --- a/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs +++ b/core/embed/rust/src/ui/model_tr/component/input_methods/wordlist.rs @@ -201,19 +201,25 @@ where } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - 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 } diff --git a/core/embed/rust/src/ui/model_tr/component/page.rs b/core/embed/rust/src/ui/model_tr/component/page.rs index 80e3a9a9d8..762a352d5d 100644 --- a/core/embed/rust/src/ui/model_tr/component/page.rs +++ b/core/embed/rust/src/ui/model_tr/component/page.rs @@ -182,7 +182,7 @@ where fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { 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() { diff --git a/core/embed/rust/src/ui/model_tr/component/result_popup.rs b/core/embed/rust/src/ui/model_tr/component/result_popup.rs index fdd2bfea7f..2d3a6eaca0 100644 --- a/core/embed/rust/src/ui/model_tr/component/result_popup.rs +++ b/core/embed/rust/src/ui/model_tr/component/result_popup.rs @@ -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; diff --git a/core/embed/rust/src/ui/model_tr/component/show_more.rs b/core/embed/rust/src/ui/model_tr/component/show_more.rs index 360f1d6903..03bc91d05c 100644 --- a/core/embed/rust/src/ui/model_tr/component/show_more.rs +++ b/core/embed/rust/src/ui/model_tr/component/show_more.rs @@ -58,7 +58,7 @@ where fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { 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);