From ff869dd86426c670a9e74696f2838d5a4c17dae9 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Fri, 21 Jun 2024 11:25:40 +0200 Subject: [PATCH] feat(core/mercury): pin entry animation [no changelog] --- core/.changelog.d/3885.added | 1 + .../model_mercury/component/keyboard/pin.rs | 357 +++++++- .../model_mercury/cshape/keyboard_overlay.rs | 71 ++ .../rust/src/ui/model_mercury/cshape/mod.rs | 4 + .../rust/src/ui/shape/cache/drawing_cache.rs | 2 +- tests/ui_tests/fixtures.json | 832 +++++++++--------- 6 files changed, 803 insertions(+), 464 deletions(-) create mode 100644 core/.changelog.d/3885.added create mode 100644 core/embed/rust/src/ui/model_mercury/cshape/keyboard_overlay.rs diff --git a/core/.changelog.d/3885.added b/core/.changelog.d/3885.added new file mode 100644 index 000000000..2773a0199 --- /dev/null +++ b/core/.changelog.d/3885.added @@ -0,0 +1 @@ +[T3T1] Added PIN keyboard animation diff --git a/core/embed/rust/src/ui/model_mercury/component/keyboard/pin.rs b/core/embed/rust/src/ui/model_mercury/component/keyboard/pin.rs index 441bcd017..06ccb44fc 100644 --- a/core/embed/rust/src/ui/model_mercury/component/keyboard/pin.rs +++ b/core/embed/rust/src/ui/model_mercury/component/keyboard/pin.rs @@ -2,24 +2,29 @@ use core::mem; use crate::{ strutil::{ShortString, TString}, - time::Duration, + time::{Duration, Stopwatch}, trezorhal::random, ui::{ component::{ - base::ComponentExt, text::TextStyle, Child, Component, Event, EventCtx, Label, Maybe, - Never, Pad, TimerToken, + base::{AttachType, ComponentExt}, + text::TextStyle, + Child, Component, Event, EventCtx, Label, Never, Pad, SwipeDirection, TimerToken, }, display::Font, event::TouchEvent, geometry::{Alignment, Alignment2D, Grid, Insets, Offset, Rect}, - model_mercury::component::{ - button::{ - Button, ButtonContent, - ButtonMsg::{self, Clicked}, + model_mercury::{ + component::{ + button::{ + Button, ButtonContent, + ButtonMsg::{self, Clicked}, + }, + theme, }, - theme, + cshape, }, shape::{self, Renderer}, + util::animation_disabled, }, }; @@ -44,18 +49,217 @@ const HEADER_PADDING: Insets = Insets::new( HEADER_PADDING_SIDE, ); +#[derive(Default, Clone)] +struct AttachAnimation { + pub attach_top: bool, + pub timer: Stopwatch, + pub active: bool, + pub duration: Duration, +} + +impl AttachAnimation { + const DURATION_MS: u32 = 750; + fn is_active(&self) -> bool { + if animation_disabled() { + return false; + } + + self.timer.is_running_within(self.duration) + } + + fn eval(&self) -> f32 { + if animation_disabled() { + return 1.0; + } + + self.timer.elapsed().to_millis() as f32 / 1000.0 + } + + fn opacity(&self, t: f32, pos_x: usize, pos_y: usize) -> u8 { + if animation_disabled() { + return 255; + } + + let diag = pos_x + pos_y; + + let start = diag as f32 * 0.05; + + let f = pareen::constant(0.0) + .seq_ease_in_out( + start, + easer::functions::Cubic, + 0.1, + pareen::constant(1.0).eval(self.eval()), + ) + .eval(t); + + (f * 255.0) as u8 + } + + fn header_opacity(&self, t: f32) -> u8 { + if animation_disabled() { + return 255; + } + let f = pareen::constant(0.0) + .seq_ease_in_out( + 0.65, + easer::functions::Linear, + 0.1, + pareen::constant(1.0).eval(self.eval()), + ) + .eval(t); + + (f * 255.0) as u8 + } + + fn start(&mut self) { + self.active = true; + self.timer.start(); + } + + fn reset(&mut self) { + self.active = false; + self.timer = Stopwatch::new_stopped(); + } + + fn lazy_start(&mut self, ctx: &mut EventCtx, event: Event) { + if let Event::Attach(_) = event { + if let Event::Attach(AttachType::Swipe(SwipeDirection::Up)) + | Event::Attach(AttachType::Swipe(SwipeDirection::Down)) + | Event::Attach(AttachType::Initial) = event + { + self.attach_top = true; + self.duration = Duration::from_millis(Self::DURATION_MS); + } else { + self.duration = Duration::from_millis(Self::DURATION_MS); + } + self.reset(); + ctx.request_anim_frame(); + } + if let Event::Timer(EventCtx::ANIM_FRAME_TIMER) = event { + if !self.timer.is_running() { + self.start(); + } + if self.is_active() { + ctx.request_anim_frame(); + ctx.request_paint(); + } else if self.active { + self.active = false; + ctx.request_anim_frame(); + ctx.request_paint(); + } + } + } +} + +#[derive(Default, Clone)] +struct CloseAnimation { + pub attach_top: bool, + pub timer: Stopwatch, + pub duration: Duration, +} +impl CloseAnimation { + const DURATION_MS: u32 = 350; + fn is_active(&self) -> bool { + if animation_disabled() { + return false; + } + + self.timer.is_running_within(self.duration) + } + + fn is_finished(&self) -> bool { + if animation_disabled() { + return true; + } + + self.timer.is_running() && !self.timer.is_running_within(self.duration) + } + + fn eval(&self) -> f32 { + if animation_disabled() { + return 1.0; + } + + self.timer.elapsed().to_millis() as f32 / 1000.0 + } + + fn opacity(&self, t: f32, pos_x: usize, pos_y: usize) -> u8 { + if animation_disabled() { + return 255; + } + + let diag = pos_x + pos_y; + + let start = diag as f32 * 0.05; + + let f = pareen::constant(1.0) + .seq_ease_in_out( + start, + easer::functions::Cubic, + 0.1, + pareen::constant(0.0).eval(self.eval()), + ) + .eval(t); + + (f * 255.0) as u8 + } + + fn header_opacity(&self, t: f32) -> u8 { + if animation_disabled() { + return 255; + } + let f = pareen::constant(1.0) + .seq_ease_in_out( + 0.10, + easer::functions::Linear, + 0.25, + pareen::constant(0.0).eval(self.eval()), + ) + .eval(t); + + (f * 255.0) as u8 + } + + fn reset(&mut self) { + self.timer = Stopwatch::new_stopped(); + } + + fn start(&mut self, ctx: &mut EventCtx) { + self.duration = Duration::from_millis(Self::DURATION_MS); + self.reset(); + self.timer.start(); + ctx.request_anim_frame(); + ctx.request_paint(); + } + fn process(&mut self, ctx: &mut EventCtx, event: Event) { + if let Event::Timer(EventCtx::ANIM_FRAME_TIMER) = event { + if self.is_active() && !self.is_finished() { + ctx.request_anim_frame(); + ctx.request_paint(); + } + } + } +} + pub struct PinKeyboard<'a> { allow_cancel: bool, + show_erase: bool, + show_cancel: bool, major_prompt: Child>, minor_prompt: Child>, major_warning: Option>>, + keypad_area: Rect, + textbox_area: Rect, textbox: Child, - textbox_pad: Pad, - erase_btn: Child>, - cancel_btn: Child>, - confirm_btn: Child