From 7b5c618576f42bad6b6fdee5baff3c7d46897685 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Tue, 19 Apr 2022 23:01:10 +0200 Subject: [PATCH] refactor(core/rust/ui): erase button clears PIN after 2s [no changelog] --- .../rust/src/ui/model_tt/component/button.rs | 36 +++++++++++++-- .../src/ui/model_tt/component/keyboard/pin.rs | 46 ++++++++++++------- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/core/embed/rust/src/ui/model_tt/component/button.rs b/core/embed/rust/src/ui/model_tt/component/button.rs index f0ce8ddece..4a0275437f 100644 --- a/core/embed/rust/src/ui/model_tt/component/button.rs +++ b/core/embed/rust/src/ui/model_tt/component/button.rs @@ -1,8 +1,11 @@ -use crate::ui::{ - component::{Component, ComponentExt, Event, EventCtx, GridPlaced, Map}, - display::{self, Color, Font}, - event::TouchEvent, - geometry::{Insets, Offset, Rect}, +use crate::{ + time::Duration, + ui::{ + component::{Component, ComponentExt, Event, EventCtx, GridPlaced, Map, TimerToken}, + display::{self, Color, Font}, + event::TouchEvent, + geometry::{Insets, Offset, Rect}, + }, }; use super::theme; @@ -11,6 +14,7 @@ pub enum ButtonMsg { Pressed, Released, Clicked, + LongPressed, } pub struct Button { @@ -18,6 +22,8 @@ pub struct Button { content: ButtonContent, styles: ButtonStyleSheet, state: State, + long_press: Option, + long_timer: Option, } impl Button { @@ -34,6 +40,8 @@ impl Button { area: Rect::zero(), styles: theme::button_default(), state: State::Initial, + long_press: None, + long_timer: None, } } @@ -54,6 +62,11 @@ impl Button { self } + pub fn with_long_press(mut self, duration: Duration) -> Self { + self.long_press = Some(duration); + self + } + pub fn enable_if(&mut self, ctx: &mut EventCtx, enabled: bool) { if enabled { self.enable(ctx); @@ -210,6 +223,9 @@ where // Touch started in our area, transform to `Pressed` state. if self.area.contains(pos) { self.set(ctx, State::Pressed); + if let Some(duration) = self.long_press { + self.long_timer = Some(ctx.request_timer(duration)); + } return Some(ButtonMsg::Pressed); } } @@ -245,6 +261,16 @@ where _ => { // Touch finished outside our area. self.set(ctx, State::Initial); + self.long_timer = None; + } + } + } + Event::Timer(token) => { + if self.long_timer == Some(token) { + self.long_timer = None; + if matches!(self.state, State::Pressed) { + self.set(ctx, State::Initial); + return Some(ButtonMsg::LongPressed); } } } diff --git a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs index 7d8ce6fbca..6a7e1e6e47 100644 --- a/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs +++ b/core/embed/rust/src/ui/model_tt/component/keyboard/pin.rs @@ -2,6 +2,7 @@ use core::{mem, ops::Deref}; use heapless::String; use crate::{ + time::Duration, trezorhal::random, ui::{ component::{ @@ -9,13 +10,13 @@ use crate::{ Pad, }, display, + event::TouchEvent, geometry::{Alignment, Grid, Insets, Offset, Rect}, model_tt::{ component::{ - button::{Button, ButtonContent, ButtonMsg::Clicked}, + button::{Button, ButtonContent, ButtonMsg, ButtonMsg::Clicked}, theme, }, - event::TouchEvent, }, }, }; @@ -27,6 +28,7 @@ pub enum PinKeyboardMsg { const MAX_LENGTH: usize = 9; const DIGIT_COUNT: usize = 10; // 0..10 +const ERASE_HOLD_DURATION: Duration = Duration::from_secs(2); const HEADER_HEIGHT: i32 = 25; const HEADER_PADDING_SIDE: i32 = 5; @@ -45,7 +47,7 @@ pub struct PinKeyboard { minor_prompt: Label, major_warning: Option>, textbox: Child, - reset_btn: Child>>, + erase_btn: Child>>, cancel_btn: Child>>, confirm_btn: Child>, digit_btns: [Child>; DIGIT_COUNT], @@ -66,10 +68,11 @@ where allow_cancel: bool, ) -> Self { // Control buttons. - let reset_btn = Button::with_icon(theme::ICON_BACK) + let erase_btn = Button::with_icon(theme::ICON_BACK) .styled(theme::button_reset()) + .with_long_press(ERASE_HOLD_DURATION) .initially_enabled(false); - let reset_btn = Maybe::hidden(theme::BG, reset_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 = @@ -82,7 +85,7 @@ where major_warning: major_warning .map(|text| Label::left_aligned(text, theme::label_keyboard_warning())), textbox: PinDots::new(theme::label_default()).into_child(), - reset_btn, + erase_btn, cancel_btn, confirm_btn: Button::with_icon(theme::ICON_CONFIRM) .styled(theme::button_confirm()) @@ -109,7 +112,7 @@ where for btn in &mut self.digit_btns { btn.mutate(ctx, |ctx, btn| btn.enable_if(ctx, !is_full)); } - self.reset_btn.mutate(ctx, |ctx, btn| { + self.erase_btn.mutate(ctx, |ctx, btn| { btn.show_if(ctx, !is_empty); btn.inner_mut().enable_if(ctx, !is_empty); }); @@ -133,7 +136,8 @@ where type Msg = PinKeyboardMsg; fn place(&mut self, bounds: Rect) -> Rect { - // Ignore the top padding for now, we need it to reliably register textbox touch events. + // Ignore the top padding for now, we need it to reliably register textbox touch + // events. let borders_no_top = Insets { top: 0, ..theme::borders() @@ -156,9 +160,9 @@ where self.major_warning.as_mut().map(|c| c.place(major_area)); // Control buttons. - let reset_cancel_area = grid.row_col(3, 0); - self.reset_btn.place(reset_cancel_area); - self.cancel_btn.place(reset_cancel_area); + let erase_cancel_area = grid.row_col(3, 0); + self.erase_btn.place(erase_cancel_area); + self.cancel_btn.place(erase_cancel_area); self.confirm_btn.place(grid.row_col(3, 2)); // Digit buttons. @@ -184,10 +188,18 @@ where if let Some(Clicked) = self.cancel_btn.event(ctx, event) { return Some(PinKeyboardMsg::Cancelled); } - if let Some(Clicked) = self.reset_btn.event(ctx, event) { - self.textbox.mutate(ctx, |ctx, t| t.clear(ctx)); - self.pin_modified(ctx); - return None; + match self.erase_btn.event(ctx, event) { + Some(ButtonMsg::Clicked) => { + self.textbox.mutate(ctx, |ctx, t| t.pop(ctx)); + self.pin_modified(ctx); + return None; + } + Some(ButtonMsg::LongPressed) => { + self.textbox.mutate(ctx, |ctx, t| t.clear(ctx)); + self.pin_modified(ctx); + return None; + } + _ => {} } for btn in &mut self.digit_btns { if let Some(Clicked) = btn.event(ctx, event) { @@ -202,7 +214,7 @@ where } fn paint(&mut self) { - self.reset_btn.paint(); + self.erase_btn.paint(); if self.textbox.inner().is_empty() { self.textbox.inner().clear_background(); if let Some(ref mut w) = self.major_warning { @@ -224,7 +236,7 @@ where fn bounds(&self, sink: &mut dyn FnMut(Rect)) { self.major_prompt.bounds(sink); self.minor_prompt.bounds(sink); - self.reset_btn.bounds(sink); + self.erase_btn.bounds(sink); self.cancel_btn.bounds(sink); self.confirm_btn.bounds(sink); self.textbox.bounds(sink);