diff --git a/core/embed/rust/src/ui/layout_eckhart/component/button.rs b/core/embed/rust/src/ui/layout_eckhart/component/button.rs index 4eb73eccf1..c0feb4c0d1 100644 --- a/core/embed/rust/src/ui/layout_eckhart/component/button.rs +++ b/core/embed/rust/src/ui/layout_eckhart/component/button.rs @@ -221,6 +221,11 @@ impl Button { self.area } + pub fn touch_area(&self) -> Rect { + self.touch_expand + .map_or(self.area, |expand| self.area.outset(expand)) + } + fn set(&mut self, ctx: &mut EventCtx, state: State) { if self.state != state { self.state = state; @@ -364,12 +369,7 @@ impl Component for Button { } fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - let touch_area = if let Some(expand) = self.touch_expand { - self.area.outset(expand) - } else { - self.area - }; - + let touch_area = self.touch_area(); match event { Event::Touch(TouchEvent::TouchStart(pos)) => { match self.state { diff --git a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs index 2421c0a702..06ade039d4 100644 --- a/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs +++ b/core/embed/rust/src/ui/layout_eckhart/component_msg_obj.rs @@ -74,6 +74,7 @@ impl ComponentMsgObj for Homescreen { fn msg_try_into_obj(&self, msg: Self::Msg) -> Result { match msg { HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()), + HomescreenMsg::Menu => Ok(INFO.as_obj()), } } } diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs index c06ec99809..7cdd78ffe2 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/action_bar.rs @@ -124,6 +124,13 @@ impl ActionBar { self.right_button.set_expanded_touch_area(expand); } + pub fn touch_area(&self) -> Rect { + let right_area = self.right_button.touch_area(); + self.left_button + .as_ref() + .map_or(right_area, |left| right_area.union(left.touch_area())) + } + pub fn update(&mut self, new_pager: Pager) { // TODO: review `clone()` of `left_content`/`right_content` if let Mode::Double { pager } = &mut self.mode { @@ -347,9 +354,7 @@ impl Component for ActionBar { btn.render(target); } self.right_button.render(target); - if let Some(htc_anim) = &self.htc_anim { - htc_anim.render(target); - } + self.htc_anim.render(target); } } diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs index af4afc9d91..d68315f23b 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/homescreen.rs @@ -9,6 +9,7 @@ use crate::{ geometry::{Insets, Offset, Rect}, layout::util::get_user_custom_image, shape::{self, Renderer}, + util::{animation_disabled, Pager}, }, }; @@ -16,7 +17,7 @@ use super::{ super::{component::Button, fonts}, constant::{HEIGHT, SCREEN, WIDTH}, theme::{self, firmware::button_homebar_style, BLACK, GREEN_DARK, GREEN_EXTRA_DARK}, - ActionBar, ActionBarMsg, Hint, + ActionBar, ActionBarMsg, Hint, HoldToConfirmAnim, }; /// Full-screen component for the homescreen and lockscreen. @@ -33,10 +34,15 @@ pub struct Homescreen { lockable: bool, /// Whether the homescreen is locked locked: bool, + /// Hold to lock button placed everywhere except the `action_bar` + virtual_locking_button: Button, + /// Hold to lock animation + htc_anim: Option, } pub enum HomescreenMsg { Dismissed, + Menu, } impl Homescreen { @@ -81,6 +87,20 @@ impl Homescreen { Button::with_homebar_content(None).styled(button_style) }; + let lock_duration = theme::LOCK_HOLD_DURATION; + + // Locking animation + let htc_anim = if lockable && !animation_disabled() { + Some( + HoldToConfirmAnim::new() + .with_color(theme::GREY_LIGHT) + .with_duration(lock_duration) + .with_header_overlay(TR::progress__locking_device.into()), + ) + } else { + None + }; + Ok(Self { label: HomeLabel::new(label), hint, @@ -88,8 +108,49 @@ impl Homescreen { image, lockable, locked, + virtual_locking_button: Button::empty().with_long_press(lock_duration), + htc_anim, }) } + + fn event_hold(&mut self, ctx: &mut EventCtx, event: Event) -> bool { + self.htc_anim.event(ctx, event); + if let Some(msg) = self.virtual_locking_button.event(ctx, event) { + match msg { + ButtonMsg::Pressed => { + if let Some(htc_anim) = &mut self.htc_anim { + htc_anim.start(); + ctx.request_anim_frame(); + ctx.request_paint(); + ctx.disable_swipe(); + } + } + ButtonMsg::Clicked => { + if let Some(htc_anim) = &mut self.htc_anim { + htc_anim.stop(); + ctx.request_anim_frame(); + ctx.request_paint(); + ctx.enable_swipe(); + } else { + // Animations disabled + return true; + } + } + ButtonMsg::Released => { + if let Some(htc_anim) = &mut self.htc_anim { + htc_anim.stop(); + ctx.request_anim_frame(); + ctx.request_paint(); + ctx.enable_swipe(); + } + } + ButtonMsg::LongPressed => { + return true; + } + } + } + false + } } impl Component for Homescreen { @@ -114,6 +175,9 @@ impl Component for Homescreen { self.label.place(label_area); self.action_bar.place(bar_area); + // Locking button is placed everywhere except the action bar + let locking_area = bounds.inset(Insets::bottom(self.action_bar.touch_area().height())); + self.virtual_locking_button.place(locking_area); bounds } @@ -122,13 +186,14 @@ impl Component for Homescreen { if self.locked { return Some(HomescreenMsg::Dismissed); } else { - // TODO: Show menu and handle "lock" action differently - if self.lockable { - return Some(HomescreenMsg::Dismissed); - } + return Some(HomescreenMsg::Menu); } } - None + if self.lockable { + Self::event_hold(self, ctx, event).then_some(HomescreenMsg::Dismissed) + } else { + None + } } fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { @@ -142,6 +207,7 @@ impl Component for Homescreen { self.label.render(target); self.hint.render(target); self.action_bar.render(target); + self.htc_anim.render(target); } } diff --git a/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs b/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs index 2484b71d17..caabc8267c 100644 --- a/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs +++ b/core/embed/rust/src/ui/layout_eckhart/theme/firmware.rs @@ -14,6 +14,7 @@ use super::{ *, }; +pub const LOCK_HOLD_DURATION: Duration = Duration::from_millis(1500); pub const CONFIRM_HOLD_DURATION: Duration = Duration::from_millis(1500); pub const ERASE_HOLD_DURATION: Duration = Duration::from_millis(1500);