From 3fcb0acaffe34c88952a9ddda34e83e78d9eb62e Mon Sep 17 00:00:00 2001 From: matejcik Date: Tue, 4 Jun 2024 14:49:11 +0200 Subject: [PATCH] refactor(core/rust): refactor SwipeFlow * remove SwipeStore in favor of Vec * unify state and controllers * implement tighter coupling between controller/states and pages of the flow --- core/embed/rust/src/ui/flow/base.rs | 80 ++++- core/embed/rust/src/ui/flow/mod.rs | 2 - core/embed/rust/src/ui/flow/store.rs | 190 ---------- core/embed/rust/src/ui/flow/swipe.rs | 324 ++++++++++++------ core/embed/rust/src/ui/layout/obj.rs | 3 +- .../ui/model_mercury/flow/confirm_action.rs | 148 ++++---- .../ui/model_mercury/flow/confirm_output.rs | 104 +++--- .../flow/confirm_reset_create.rs | 68 ++-- .../flow/confirm_reset_recover.rs | 75 ++-- .../model_mercury/flow/confirm_set_new_pin.rs | 93 ++--- .../ui/model_mercury/flow/confirm_summary.rs | 110 +++--- .../src/ui/model_mercury/flow/get_address.rs | 154 +++------ .../ui/model_mercury/flow/prompt_backup.rs | 97 ++---- .../ui/model_mercury/flow/request_number.rs | 72 ++-- .../ui/model_mercury/flow/show_share_words.rs | 66 ++-- .../ui/model_mercury/flow/show_tutorial.rs | 118 +++---- .../ui/model_mercury/flow/warning_hi_prio.rs | 70 ++-- core/embed/rust/src/ui/shape/mod.rs | 4 +- 18 files changed, 748 insertions(+), 1030 deletions(-) delete mode 100644 core/embed/rust/src/ui/flow/store.rs diff --git a/core/embed/rust/src/ui/flow/base.rs b/core/embed/rust/src/ui/flow/base.rs index 6f25dbfdb..a0e29c91c 100644 --- a/core/embed/rust/src/ui/flow/base.rs +++ b/core/embed/rust/src/ui/flow/base.rs @@ -1,5 +1,4 @@ use crate::ui::component::{swipe_detect::SwipeConfig, SwipeDirection}; -use num_traits::ToPrimitive; pub trait Swipable { fn get_swipe_config(&self) -> SwipeConfig; @@ -22,20 +21,20 @@ pub enum FlowMsg { /// Composable event handler result. #[derive(Copy, Clone)] -pub enum Decision { +pub enum Decision { /// Do nothing, continue with processing next handler. Nothing, /// Initiate transition to another state, end event processing. /// NOTE: it might make sense to include Option here - Goto(Q, SwipeDirection), + Transition(SwipeDirection), /// Yield a message to the caller of the flow (i.e. micropython), end event /// processing. Return(FlowMsg), } -impl Decision { +impl Decision { pub fn or_else(self, func: impl FnOnce() -> Self) -> Self { match self { Decision::Nothing => func(), @@ -44,24 +43,71 @@ impl Decision { } } +/// State transition type. +/// +/// Contains a new state (by convention it must be of the same concrete type as +/// the current one) and a Decision object that tells the flow what to do next. +pub type StateChange = (&'static dyn FlowState, Decision); + /// Encodes the flow logic as a set of states, and transitions between them /// triggered by events and swipes. -pub trait FlowState -where - Self: Sized + Copy + Eq + ToPrimitive, -{ - /// There needs to be a mapping from states to indices of the FlowStore - /// array. Default implementation works for states that are enums, the - /// FlowStore has to have number of elements equal to number of states. - fn index(&self) -> usize { - unwrap!(self.to_usize()) - } - +pub trait FlowState { /// What to do when user swipes on screen and current component doesn't /// respond to swipe of that direction. - fn handle_swipe(&self, direction: SwipeDirection) -> Decision; + /// + /// By convention, the type of the new state inside the state change must be + /// Self. This can't be enforced by the type system unfortunately, because + /// this trait must remain object-safe and so can't refer to Self. + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange; /// What to do when the current component emits a message in response to an /// event. - fn handle_event(&self, msg: FlowMsg) -> Decision; + /// + /// By convention, the type of the new state inside the state change must be + /// Self. This can't be enforced by the type system unfortunately, because + /// this trait must remain object-safe and so can't refer to Self. + fn handle_event(&'static self, msg: FlowMsg) -> StateChange; + + /// Page index of the current state. + fn index(&'static self) -> usize; } + +/// Helper trait for writing nicer flow logic. +pub trait DecisionBuilder: FlowState + Sized { + #[inline] + fn swipe(&'static self, direction: SwipeDirection) -> StateChange { + (self, Decision::Transition(direction)) + } + + #[inline] + fn swipe_left(&'static self) -> StateChange { + self.swipe(SwipeDirection::Left) + } + + #[inline] + fn swipe_right(&'static self) -> StateChange { + self.swipe(SwipeDirection::Right) + } + + #[inline] + fn swipe_up(&'static self) -> StateChange { + self.swipe(SwipeDirection::Up) + } + + #[inline] + fn swipe_down(&'static self) -> StateChange { + self.swipe(SwipeDirection::Down) + } + + #[inline] + fn do_nothing(&'static self) -> StateChange { + (self, Decision::Nothing) + } + + #[inline] + fn return_msg(&'static self, msg: FlowMsg) -> StateChange { + (self, Decision::Return(msg)) + } +} + +impl DecisionBuilder for T {} diff --git a/core/embed/rust/src/ui/flow/mod.rs b/core/embed/rust/src/ui/flow/mod.rs index a16585479..0a7ca047e 100644 --- a/core/embed/rust/src/ui/flow/mod.rs +++ b/core/embed/rust/src/ui/flow/mod.rs @@ -1,9 +1,7 @@ pub mod base; pub mod page; -mod store; mod swipe; pub use base::{FlowMsg, FlowState, Swipable}; pub use page::SwipePage; -pub use store::{flow_store, FlowStore}; pub use swipe::SwipeFlow; diff --git a/core/embed/rust/src/ui/flow/store.rs b/core/embed/rust/src/ui/flow/store.rs deleted file mode 100644 index 188eab529..000000000 --- a/core/embed/rust/src/ui/flow/store.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::{ - error, - maybe_trace::MaybeTrace, - ui::{ - component::{Component, Event, EventCtx}, - flow::base::FlowMsg, - geometry::Rect, - shape::Renderer, - }, -}; - -use crate::{ - micropython::gc::Gc, - ui::{component::swipe_detect::SwipeConfig, flow::Swipable}, -}; - -/// `FlowStore` is essentially `Vec>` except -/// that `trait Component` is not object-safe so it ends up being a kind of -/// recursively-defined tuple. -/// Implementors are something like the V in MVC. -pub trait FlowStore { - /// Call `Component::place` on all elements. - fn place(&mut self, bounds: Rect) -> Rect; - - /// Call `Component::event` on i-th element. - fn event(&mut self, i: usize, ctx: &mut EventCtx, event: Event) -> Option; - - /// Call `Component::render` on i-th element. - fn render<'s>(&'s self, i: usize, target: &mut impl Renderer<'s>); - - #[cfg(feature = "ui_debug")] - /// Call `Trace::trace` on i-th element. - fn trace(&self, i: usize, t: &mut dyn crate::trace::Tracer); - - /// Forward `SimpleSwipable` methods to i-th element. - fn map_swipable(&mut self, i: usize, func: impl FnOnce(&mut dyn Swipable) -> T) -> T; - - fn get_swipe_config(&self, i: usize) -> SwipeConfig; - - fn get_internal_page_count(&mut self, i: usize) -> usize; - - /// Add a Component to the end of a `FlowStore`. - fn add + MaybeTrace + Swipable>( - self, - elem: E, - ) -> Result - where - Self: Sized; -} - -/// Create new empty flow store. -pub fn flow_store() -> impl FlowStore { - FlowEmpty {} -} - -/// Terminating element of a recursive structure. -struct FlowEmpty; - -// Methods that take an index panic because it's always out of bounds. -impl FlowStore for FlowEmpty { - fn place(&mut self, bounds: Rect) -> Rect { - bounds - } - - fn event(&mut self, _i: usize, _ctx: &mut EventCtx, _event: Event) -> Option { - panic!() - } - - fn render<'s>(&self, _i: usize, _target: &mut impl Renderer<'s>) { - panic!() - } - - #[cfg(feature = "ui_debug")] - fn trace(&self, _i: usize, _t: &mut dyn crate::trace::Tracer) { - panic!() - } - - fn map_swipable(&mut self, _i: usize, _func: impl FnOnce(&mut dyn Swipable) -> T) -> T { - panic!() - } - - fn add + MaybeTrace + Swipable>( - self, - elem: E, - ) -> Result - where - Self: Sized, - { - Ok(FlowComponent2 { - elem: Gc::new(elem)?, - next: Self, - }) - } - fn get_swipe_config(&self, _i: usize) -> SwipeConfig { - SwipeConfig::new() - } - fn get_internal_page_count(&mut self, _i: usize) -> usize { - 1 - } -} - -struct FlowComponent2, P> { - /// Component allocated on micropython heap. - pub elem: Gc, - - /// Nested FlowStore. - pub next: P, -} - -impl, P> FlowComponent2 { - fn as_ref(&self) -> &E { - &self.elem - } - - fn as_mut(&mut self) -> &mut E { - // SAFETY: micropython can only access this object through LayoutObj which wraps - // us in a RefCell which guarantees uniqueness - unsafe { Gc::as_mut(&mut self.elem) } - } -} - -impl FlowStore for FlowComponent2 -where - E: Component + MaybeTrace + Swipable, - P: FlowStore, -{ - fn place(&mut self, bounds: Rect) -> Rect { - self.as_mut().place(bounds); - self.next.place(bounds); - bounds - } - - fn event(&mut self, i: usize, ctx: &mut EventCtx, event: Event) -> Option { - if i == 0 { - self.as_mut().event(ctx, event) - } else { - self.next.event(i - 1, ctx, event) - } - } - - fn render<'s>(&'s self, i: usize, target: &mut impl Renderer<'s>) { - if i == 0 { - self.as_ref().render(target) - } else { - self.next.render(i - 1, target) - } - } - - #[cfg(feature = "ui_debug")] - fn trace(&self, i: usize, t: &mut dyn crate::trace::Tracer) { - if i == 0 { - self.as_ref().trace(t) - } else { - self.next.trace(i - 1, t) - } - } - - fn map_swipable(&mut self, i: usize, func: impl FnOnce(&mut dyn Swipable) -> T) -> T { - if i == 0 { - func(self.as_mut()) - } else { - self.next.map_swipable(i - 1, func) - } - } - - fn add + MaybeTrace + Swipable>( - self, - elem: F, - ) -> Result - where - Self: Sized, - { - Ok(FlowComponent2 { - elem: self.elem, - next: self.next.add(elem)?, - }) - } - - fn get_swipe_config(&self, i: usize) -> SwipeConfig { - if i == 0 { - self.as_ref().get_swipe_config() - } else { - self.next.get_swipe_config(i - 1) - } - } - - fn get_internal_page_count(&mut self, i: usize) -> usize { - self.map_swipable(i, |swipable| swipable.get_internal_page_count()) - } -} diff --git a/core/embed/rust/src/ui/flow/swipe.rs b/core/embed/rust/src/ui/flow/swipe.rs index 1ec8a8010..396681911 100644 --- a/core/embed/rust/src/ui/flow/swipe.rs +++ b/core/embed/rust/src/ui/flow/swipe.rs @@ -1,29 +1,103 @@ use crate::{ - error, + error::{self, Error}, + maybe_trace::MaybeTrace, + micropython::{ + gc::{self, GcBox}, + obj::Obj, + }, ui::{ component::{ base::AttachType, swipe_detect::SwipeSettings, Component, Event, EventCtx, SwipeDetect, SwipeDetectMsg, SwipeDirection, }, + display::Color, event::{SwipeEvent, TouchEvent}, - flow::{base::Decision, FlowMsg, FlowState, FlowStore}, + flow::{base::Decision, FlowMsg, FlowState}, geometry::Rect, - shape::Renderer, + layout::obj::ObjComponent, + shape::{render_on_display, ConcreteRenderer, Renderer, ScopedRenderer}, util::animation_disabled, }, }; -/// Given a state enum and a corresponding FlowStore, create a Component that -/// implements a swipe navigation between the states with animated transitions. +use super::{base::StateChange, Swipable}; + +use heapless::Vec; + +/// Component-like proto-object-safe trait. +/// +/// This copies the Component interface, but it is parametrized by a concrete +/// Renderer type, making it object-safe. +pub trait FlowComponentTrait<'s, R: Renderer<'s>>: Swipable { + fn place(&mut self, bounds: Rect) -> Rect; + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option; + fn render(&'s self, target: &mut R); + + #[cfg(feature = "ui_debug")] + fn trace(&self, t: &mut dyn crate::trace::Tracer); +} + +/// FlowComponentTrait implementation for Components. +/// +/// Components implementing FlowComponentTrait must: +/// * also implement Swipable, required by FlowComponentTrait, +/// * use FlowMsg as their Msg type, +/// * implement MaybeTrace to be able to conform to ObjComponent. +impl<'s, R, C> FlowComponentTrait<'s, R> for C +where + C: Component + MaybeTrace + Swipable, + R: Renderer<'s>, +{ + fn place(&mut self, bounds: Rect) -> Rect { + ::place(self, bounds) + } + + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + ::event(self, ctx, event) + } + + fn render(&'s self, target: &mut R) { + ::render(self, target) + } + + #[cfg(feature = "ui_debug")] + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + ::trace(self, t) + } +} + +/// Shortcut type for the concrete renderer passed into `render()` method. +type RendererImpl<'a, 'alloc, 'env> = ScopedRenderer<'alloc, 'env, ConcreteRenderer<'a, 'alloc>>; + +/// Fully object-safe component-like trait for flow components. +/// +/// This trait has no generic parameters: +/// * it is instantiated with a concrete Renderer type, and +/// * it requires the `FlowComponentTrait` trait to be implemented for _any_ +/// lifetimes. +pub trait FlowComponentDynTrait: + for<'a, 'alloc, 'env> FlowComponentTrait<'alloc, RendererImpl<'a, 'alloc, 'env>> +{ +} + +impl FlowComponentDynTrait for T where + T: for<'a, 'alloc, 'env> FlowComponentTrait<'alloc, RendererImpl<'a, 'alloc, 'env>> +{ +} + +/// Swipe flow consisting of multiple screens. +/// +/// Implements swipe navigation between the states with animated transitions, +/// based on state transitions provided by the FlowState type. /// /// If a swipe is detected: /// - currently active component is asked to handle the event, /// - if it can't then FlowState::handle_swipe is consulted. -pub struct SwipeFlow { - /// Current state. - state: Q, - /// FlowStore with all screens/components. - store: S, +pub struct SwipeFlow { + /// Current state of the flow. + state: &'static dyn FlowState, + /// Store of all screens which are part of the flow. + store: Vec, 10>, /// Swipe detector. swipe: SwipeDetect, /// Swipe allowed @@ -34,33 +108,53 @@ pub struct SwipeFlow { internal_pages: u16, /// If triggering swipe by event, make this decision instead of default /// after the swipe. - decision_override: Option>, + decision_override: Option, } -impl SwipeFlow { - pub fn new(init: Q, store: S) -> Result { +impl SwipeFlow { + pub fn new(initial_state: &'static dyn FlowState) -> Result { Ok(Self { - state: init, + state: initial_state, swipe: SwipeDetect::new(), - store, + store: Vec::new(), allow_swipe: true, internal_state: 0, internal_pages: 1, decision_override: None, }) } - fn goto(&mut self, ctx: &mut EventCtx, direction: SwipeDirection, state: Q) { - self.state = state; + + /// Add a page to the flow. + /// + /// Pages must be inserted in the order of the flow state index. + pub fn with_page( + mut self, + state: &'static dyn FlowState, + page: impl FlowComponentDynTrait + 'static, + ) -> Result { + debug_assert!(self.store.len() == state.index()); + let alloc = GcBox::new(page)?; + let page = gc::coerce!(FlowComponentDynTrait, alloc); + unwrap!(self.store.push(page)); + Ok(self) + } + + fn current_page(&self) -> &GcBox { + &self.store[self.state.index()] + } + + fn current_page_mut(&mut self) -> &mut GcBox { + &mut self.store[self.state.index()] + } + + fn goto(&mut self, ctx: &mut EventCtx, direction: SwipeDirection) { self.swipe = SwipeDetect::new(); self.allow_swipe = true; - self.store.event( - state.index(), - ctx, - Event::Attach(AttachType::Swipe(direction)), - ); + self.current_page_mut() + .event(ctx, Event::Attach(AttachType::Swipe(direction))); - self.internal_pages = self.store.get_internal_page_count(state.index()) as u16; + self.internal_pages = self.current_page_mut().get_internal_page_count() as u16; match direction { SwipeDirection::Up => { @@ -75,46 +169,43 @@ impl SwipeFlow { ctx.request_paint(); } - fn render_state<'s>(&'s self, state: Q, target: &mut impl Renderer<'s>) { - self.store.render(state.index(), target) + fn render_state<'s>(&'s self, state: usize, target: &mut RendererImpl<'_, 's, '_>) { + self.store[state].render(target); } fn handle_swipe_child( &mut self, _ctx: &mut EventCtx, direction: SwipeDirection, - ) -> Decision { + ) -> StateChange { self.state.handle_swipe(direction) } - fn handle_event_child(&mut self, ctx: &mut EventCtx, event: Event) -> Decision { - let msg = self.store.event(self.state.index(), ctx, event); + fn handle_event_child(&mut self, ctx: &mut EventCtx, event: Event) -> StateChange { + let msg = self.current_page_mut().event(ctx, event); if let Some(msg) = msg { self.state.handle_event(msg) } else { - Decision::Nothing + (self.state, Decision::Nothing) } } -} -impl Component for SwipeFlow { - type Msg = FlowMsg; - - fn place(&mut self, bounds: Rect) -> Rect { - self.store.place(bounds) + fn state_unchanged(&self) -> StateChange { + (self.state, Decision::Nothing) } - fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { - let mut decision: Decision = Decision::Nothing; + fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option { + let mut state_change = self.state_unchanged(); let mut return_transition: AttachType = AttachType::Initial; let mut attach = false; let e = if self.allow_swipe { - let mut config = self.store.get_swipe_config(self.state.index()); + let page = self.current_page(); + let mut config = page.get_swipe_config(); - self.internal_pages = self.store.get_internal_page_count(self.state.index()) as u16; + self.internal_pages = page.get_internal_page_count() as u16; // add additional swipe directions if there are more internal pages // todo can we get internal settings from config somehow? @@ -135,9 +226,9 @@ impl Component for SwipeFlow { match self.swipe.event(ctx, event, config) { Some(SwipeDetectMsg::Trigger(dir)) => { if let Some(override_decision) = self.decision_override.take() { - decision = override_decision; + state_change = override_decision; } else { - decision = self.handle_swipe_child(ctx, dir); + state_change = self.handle_swipe_child(ctx, dir); } return_transition = AttachType::Swipe(dir); @@ -148,11 +239,11 @@ impl Component for SwipeFlow { let current_state = self.internal_state; if dir == SwipeDirection::Left && current_state < states_num - 1 { self.internal_state += 1; - decision = Decision::Nothing; + state_change = self.state_unchanged(); attach = true; } else if dir == SwipeDirection::Right && current_state > 0 { self.internal_state -= 1; - decision = Decision::Nothing; + state_change = self.state_unchanged(); attach = true; } } @@ -160,77 +251,75 @@ impl Component for SwipeFlow { let current_state = self.internal_state; if dir == SwipeDirection::Up && current_state < states_num - 1 { self.internal_state += 1; - decision = Decision::Nothing; + state_change = self.state_unchanged(); attach = true; } else if dir == SwipeDirection::Down && current_state > 0 { self.internal_state -= 1; - decision = Decision::Nothing; + state_change = self.state_unchanged(); attach = true; } } } - Some(Event::Swipe(SwipeEvent::End(dir))) + Event::Swipe(SwipeEvent::End(dir)) } Some(SwipeDetectMsg::Move(dir, progress)) => { - Some(Event::Swipe(SwipeEvent::Move(dir, progress as i16))) + Event::Swipe(SwipeEvent::Move(dir, progress as i16)) } - Some(SwipeDetectMsg::Start(_)) => Some(Event::Touch(TouchEvent::TouchAbort)), - _ => Some(event), + Some(SwipeDetectMsg::Start(_)) => Event::Touch(TouchEvent::TouchAbort), + _ => event, } } else { - Some(event) + event }; - if let Some(e) = e { - match decision { - Decision::Nothing => { - decision = self.handle_event_child(ctx, e); - - // when doing internal transition, pass attach event to the child after sending - // swipe end. - if attach { - if let Event::Swipe(SwipeEvent::End(dir)) = e { - self.store.event( - self.state.index(), - ctx, - Event::Attach(AttachType::Swipe(dir)), - ); - } - } + match state_change { + (_, Decision::Nothing) => { + state_change = self.handle_event_child(ctx, e); - if ctx.disable_swipe_requested() { - self.swipe.reset(); - self.allow_swipe = false; + // when doing internal transition, pass attach event to the child after sending + // swipe end. + if attach { + if let Event::Swipe(SwipeEvent::End(dir)) = e { + self.current_page_mut() + .event(ctx, Event::Attach(AttachType::Swipe(dir))); } - if ctx.enable_swipe_requested() { - self.swipe.reset(); - self.allow_swipe = true; - }; + } - let config = self.store.get_swipe_config(self.state.index()); + if ctx.disable_swipe_requested() { + self.swipe.reset(); + self.allow_swipe = false; + } + if ctx.enable_swipe_requested() { + self.swipe.reset(); + self.allow_swipe = true; + }; - if let Decision::Goto(_, direction) = decision { - if config.is_allowed(direction) { - if !animation_disabled() { - self.swipe.trigger(ctx, direction, config); - self.decision_override = Some(decision); - decision = Decision::Nothing; - } - self.allow_swipe = true; + let config = self.current_page().get_swipe_config(); + + if let (_, Decision::Transition(direction)) = state_change { + if config.is_allowed(direction) { + if !animation_disabled() { + self.swipe.trigger(ctx, direction, config); + self.decision_override = Some(state_change); + state_change = self.state_unchanged(); } + self.allow_swipe = true; } } - _ => { - //ignore message, we are already transitioning - self.store.event(self.state.index(), ctx, event); - } + } + _ => { + //ignore message, we are already transitioning + self.current_page_mut().event(ctx, event); } } + let (new_state, decision) = state_change; + self.state = new_state; match decision { - Decision::Goto(next_state, direction) => { - self.goto(ctx, direction, next_state); + Decision::Transition(direction) => { + self.state = new_state; + self.goto(ctx, direction); None } Decision::Return(msg) => { @@ -242,34 +331,49 @@ impl Component for SwipeFlow { _ => None, } } - - fn paint(&mut self) {} - - fn render<'s>(&'s self, target: &mut impl Renderer<'s>) { - self.render_state(self.state, target); - } } -#[cfg(feature = "ui_debug")] -impl crate::trace::Trace for SwipeFlow { - fn trace(&self, t: &mut dyn crate::trace::Tracer) { - self.store.trace(self.state.index(), t) +/// ObjComponent implementation for SwipeFlow. +/// +/// Instead of using the generic `impl ObjComponent for ComponentMsgObj`, we +/// provide our own short-circuit implementation for `SwipeFlow`. This way we +/// can completely avoid implementing `Component`. That also allows us to pass +/// around concrete Renderers instead of having to conform to `Component`'s +/// not-object-safe interface. +/// +/// This implementation relies on the fact that swipe components always return +/// `FlowMsg` as their `Component::Msg` (provided by `impl FlowComponentTrait` +/// earlier in this file). +#[cfg(feature = "micropython")] +impl ObjComponent for SwipeFlow { + fn obj_place(&mut self, bounds: Rect) -> Rect { + for elem in self.store.iter_mut() { + elem.place(bounds); + } + bounds } -} -#[cfg(feature = "micropython")] -impl crate::ui::layout::obj::ComponentMsgObj for SwipeFlow { - fn msg_try_into_obj( - &self, - msg: Self::Msg, - ) -> Result { - match msg { - FlowMsg::Confirmed => Ok(crate::ui::layout::result::CONFIRMED.as_obj()), - FlowMsg::Cancelled => Ok(crate::ui::layout::result::CANCELLED.as_obj()), - FlowMsg::Info => Ok(crate::ui::layout::result::INFO.as_obj()), - FlowMsg::Choice(i) => { + fn obj_event(&mut self, ctx: &mut EventCtx, event: Event) -> Result { + match self.event(ctx, event) { + None => Ok(Obj::const_none()), + Some(FlowMsg::Confirmed) => Ok(crate::ui::layout::result::CONFIRMED.as_obj()), + Some(FlowMsg::Cancelled) => Ok(crate::ui::layout::result::CANCELLED.as_obj()), + Some(FlowMsg::Info) => Ok(crate::ui::layout::result::INFO.as_obj()), + Some(FlowMsg::Choice(i)) => { Ok((crate::ui::layout::result::CONFIRMED.as_obj(), i.try_into()?).try_into()?) } } } + fn obj_paint(&mut self) { + render_on_display(None, Some(Color::black()), |target| { + self.render_state(self.state.index(), target); + }); + } +} + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for SwipeFlow { + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + self.current_page().trace(t) + } } diff --git a/core/embed/rust/src/ui/layout/obj.rs b/core/embed/rust/src/ui/layout/obj.rs index c81124b90..665b903ba 100644 --- a/core/embed/rust/src/ui/layout/obj.rs +++ b/core/embed/rust/src/ui/layout/obj.rs @@ -319,8 +319,7 @@ impl LayoutObjInner { impl LayoutObj { /// Create a new `LayoutObj`, wrapping a root component. - #[inline(never)] - pub fn new(root: impl ComponentMsgObj + MaybeTrace + 'static) -> Result, Error> { + pub fn new(root: impl ObjComponent + 'static) -> Result, Error> { // SAFETY: This is a Python object and hase a base as first element unsafe { Gc::new_with_custom_finaliser(Self { diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs index ce29e42ba..724e162b6 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs @@ -1,123 +1,101 @@ use crate::{ error, maybe_trace::MaybeTrace, + micropython::{map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ - component::{text::paragraphs::Paragraph, ComponentExt, SwipeDirection}, - flow::{base::Decision, FlowMsg, FlowState, FlowStore}, + component::{ + swipe_detect::SwipeSettings, + text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt}, + Component, ComponentExt, Paginate, SwipeDirection, + }, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, SwipePage, + }, + layout::obj::LayoutObj, }, }; use super::super::{ - component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg}, + component::{Frame, FrameMsg, PromptScreen, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg}, theme, }; // TODO: merge with code from https://github.com/trezor/trezor-firmware/pull/3805 // when ready -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmAction { Intro, Menu, Confirm, } -/// ConfirmAction flow without a separate "Tap to confirm" or "Hold to confirm" -/// screen. Swiping up directly from the intro screen confirms action. -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] -pub enum ConfirmActionSimple { - Intro, - Menu, -} - impl FlowState for ConfirmAction { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmAction::Intro, SwipeDirection::Left) => { - Decision::Goto(ConfirmAction::Menu, direction) - } - (ConfirmAction::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmAction::Intro, direction) - } - (ConfirmAction::Intro, SwipeDirection::Up) => { - Decision::Goto(ConfirmAction::Confirm, direction) - } - (ConfirmAction::Confirm, SwipeDirection::Down) => { - Decision::Goto(ConfirmAction::Intro, direction) - } - (ConfirmAction::Confirm, SwipeDirection::Left) => { - Decision::Goto(ConfirmAction::Menu, direction) - } - _ => Decision::Nothing, + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::Intro, SwipeDirection::Up) => Self::Confirm.swipe(direction), + (Self::Confirm, SwipeDirection::Down) => Self::Intro.swipe(direction), + (Self::Confirm, SwipeDirection::Left) => Self::Menu.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ConfirmAction::Intro, FlowMsg::Info) => { - Decision::Goto(ConfirmAction::Menu, SwipeDirection::Left) - } - (ConfirmAction::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmAction::Intro, SwipeDirection::Right) - } - (ConfirmAction::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), - (ConfirmAction::Menu, FlowMsg::Choice(1)) => Decision::Return(FlowMsg::Info), - (ConfirmAction::Confirm, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed), - (ConfirmAction::Confirm, FlowMsg::Info) => { - Decision::Goto(ConfirmAction::Menu, SwipeDirection::Left) - } - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info), + (Self::Confirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Confirm, FlowMsg::Info) => Self::Menu.swipe_left(), + _ => self.do_nothing(), } } } +/// ConfirmAction flow without a separate "Tap to confirm" or "Hold to confirm" +/// screen. Swiping up directly from the intro screen confirms action. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ConfirmActionSimple { + Intro, + Menu, +} + impl FlowState for ConfirmActionSimple { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmActionSimple::Intro, SwipeDirection::Left) => { - Decision::Goto(ConfirmActionSimple::Menu, direction) - } - (ConfirmActionSimple::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmActionSimple::Intro, direction) - } - (ConfirmActionSimple::Intro, SwipeDirection::Up) => { - Decision::Return(FlowMsg::Confirmed) - } - _ => Decision::Nothing, + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::Intro, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ConfirmActionSimple::Intro, FlowMsg::Info) => { - Decision::Goto(ConfirmActionSimple::Menu, SwipeDirection::Left) - } - (ConfirmActionSimple::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmActionSimple::Intro, SwipeDirection::Right) - } - (ConfirmActionSimple::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), - (ConfirmActionSimple::Menu, FlowMsg::Choice(1)) => Decision::Return(FlowMsg::Info), - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Info), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, qstr::Qstr, util}, - ui::{ - component::{ - swipe_detect::SwipeSettings, - text::paragraphs::{ParagraphSource, ParagraphVecShort, VecExt}, - Component, Paginate, - }, - flow::{flow_store, SwipeFlow, SwipePage}, - layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, new_confirm_action_obj) } @@ -248,15 +226,15 @@ pub fn new_confirm_action_uni( FrameMsg::Button(_) => Some(FlowMsg::Info), }); - let store = flow_store() - .add(content_intro)? - .add(content_menu)? - .add(content_confirm)?; - let res = SwipeFlow::new(ConfirmAction::Intro, store)?; + let res = SwipeFlow::new(&ConfirmAction::Intro)? + .with_page(&ConfirmAction::Intro, content_intro)? + .with_page(&ConfirmAction::Menu, content_menu)? + .with_page(&ConfirmAction::Confirm, content_confirm)?; Ok(LayoutObj::new(res)?.into()) } else { - let store = flow_store().add(content_intro)?.add(content_menu)?; - let res = SwipeFlow::new(ConfirmActionSimple::Intro, store)?; + let res = SwipeFlow::new(&ConfirmActionSimple::Intro)? + .with_page(&ConfirmActionSimple::Intro, content_intro)? + .with_page(&ConfirmActionSimple::Menu, content_menu)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs index 12caa1a21..a8b612364 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_output.rs @@ -1,25 +1,31 @@ use crate::{ error, - micropython::qstr::Qstr, + micropython::{map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ button_request::ButtonRequest, - component::{ButtonRequestExt, ComponentExt, SwipeDirection}, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + component::{swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt, SwipeDirection}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; -use super::super::{ - component::{ - AddressDetails, Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg, +use super::{ + super::{ + component::{ + AddressDetails, Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg, + }, + theme, }, - theme, + util::ConfirmBlobParams, }; -use super::util::ConfirmBlobParams; - -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmOutput { Address, Amount, @@ -30,58 +36,39 @@ pub enum ConfirmOutput { } impl FlowState for ConfirmOutput { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmOutput::Address | ConfirmOutput::Amount, SwipeDirection::Left) => { - Decision::Goto(ConfirmOutput::Menu, direction) - } - (ConfirmOutput::Address, SwipeDirection::Up) => { - Decision::Goto(ConfirmOutput::Amount, direction) - } - (ConfirmOutput::Amount, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed), - (ConfirmOutput::Amount, SwipeDirection::Down) => { - Decision::Goto(ConfirmOutput::Address, direction) - } - (ConfirmOutput::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmOutput::Address, direction) - } - (ConfirmOutput::Menu, SwipeDirection::Left) => { - Decision::Goto(ConfirmOutput::AccountInfo, direction) - } - (ConfirmOutput::AccountInfo | ConfirmOutput::CancelTap, SwipeDirection::Right) => { - Decision::Goto(ConfirmOutput::Menu, direction) + (Self::Address | Self::Amount, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Address, SwipeDirection::Up) => Self::Amount.swipe(direction), + (Self::Amount, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + (Self::Amount, SwipeDirection::Down) => Self::Address.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Address.swipe(direction), + (Self::Menu, SwipeDirection::Left) => Self::AccountInfo.swipe(direction), + (Self::AccountInfo | Self::CancelTap, SwipeDirection::Right) => { + Self::Menu.swipe(direction) } - _ => Decision::Nothing, + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (_, FlowMsg::Info) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Left), - (ConfirmOutput::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(ConfirmOutput::AccountInfo, SwipeDirection::Left) - } - (ConfirmOutput::Menu, FlowMsg::Choice(1)) => { - Decision::Goto(ConfirmOutput::CancelTap, SwipeDirection::Left) - } - (ConfirmOutput::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmOutput::Address, SwipeDirection::Right) - } - (ConfirmOutput::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled), - (_, FlowMsg::Cancelled) => Decision::Goto(ConfirmOutput::Menu, SwipeDirection::Right), - _ => Decision::Nothing, + (_, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(0)) => Self::AccountInfo.swipe_left(), + (Self::Menu, FlowMsg::Choice(1)) => Self::CancelTap.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(), + (Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + (_, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_confirm_output(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmOutput::new_obj) } @@ -156,13 +143,12 @@ impl ConfirmOutput { FrameMsg::Button(_) => Some(FlowMsg::Cancelled), }); - let store = flow_store() - .add(content_address)? - .add(content_amount)? - .add(content_menu)? - .add(content_account)? - .add(content_cancel_tap)?; - let res = SwipeFlow::new(ConfirmOutput::Address, store)?; + let res = SwipeFlow::new(&ConfirmOutput::Address)? + .with_page(&ConfirmOutput::Address, content_address)? + .with_page(&ConfirmOutput::Amount, content_amount)? + .with_page(&ConfirmOutput::Menu, content_menu)? + .with_page(&ConfirmOutput::AccountInfo, content_account)? + .with_page(&ConfirmOutput::CancelTap, content_cancel_tap)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_create.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_create.rs index e02ba7c28..513f740d3 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_create.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_create.rs @@ -10,7 +10,10 @@ use crate::{ text::paragraphs::{Paragraph, Paragraphs}, ButtonRequestExt, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, layout::obj::LayoutObj, model_mercury::component::{PromptScreen, SwipeContent}, }, @@ -21,7 +24,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmResetCreate { Intro, Menu, @@ -29,43 +32,30 @@ pub enum ConfirmResetCreate { } impl FlowState for ConfirmResetCreate { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmResetCreate::Intro, SwipeDirection::Left) => { - Decision::Goto(ConfirmResetCreate::Menu, direction) - } - (ConfirmResetCreate::Intro, SwipeDirection::Up) => { - Decision::Goto(ConfirmResetCreate::Confirm, direction) - } - (ConfirmResetCreate::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmResetCreate::Intro, direction) - } - (ConfirmResetCreate::Confirm, SwipeDirection::Down) => { - Decision::Goto(ConfirmResetCreate::Intro, direction) - } - (ConfirmResetCreate::Confirm, SwipeDirection::Left) => { - Decision::Goto(ConfirmResetCreate::Menu, direction) - } - _ => Decision::Nothing, + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Intro, SwipeDirection::Up) => Self::Confirm.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::Confirm, SwipeDirection::Down) => Self::Intro.swipe(direction), + (Self::Confirm, SwipeDirection::Left) => Self::Menu.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ConfirmResetCreate::Intro, FlowMsg::Info) => { - Decision::Goto(ConfirmResetCreate::Menu, SwipeDirection::Left) - } - (ConfirmResetCreate::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmResetCreate::Intro, SwipeDirection::Right) - } - (ConfirmResetCreate::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), - (ConfirmResetCreate::Confirm, FlowMsg::Confirmed) => { - Decision::Return(FlowMsg::Confirmed) - } - (ConfirmResetCreate::Confirm, FlowMsg::Info) => { - Decision::Goto(ConfirmResetCreate::Menu, SwipeDirection::Left) - } - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + (Self::Confirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Confirm, FlowMsg::Info) => Self::Menu.swipe_left(), + _ => self.do_nothing(), } } } @@ -124,12 +114,10 @@ impl ConfirmResetCreate { }) .one_button_request(ButtonRequestCode::ResetDevice.with_type("confirm_setup_device")); - let store = flow_store() - .add(content_intro)? - .add(content_menu)? - .add(content_confirm)?; - - let res = SwipeFlow::new(ConfirmResetCreate::Intro, store)?; + let res = SwipeFlow::new(&ConfirmResetCreate::Intro)? + .with_page(&ConfirmResetCreate::Intro, content_intro)? + .with_page(&ConfirmResetCreate::Menu, content_menu)? + .with_page(&ConfirmResetCreate::Confirm, content_confirm)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_recover.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_recover.rs index 4a76b3e6c..97ce967e2 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_recover.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_reset_recover.rs @@ -1,13 +1,20 @@ use crate::{ error, + micropython::{map::Map, obj::Obj, util}, translations::TR, ui::{ button_request::ButtonRequestCode, component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, Paragraphs}, ButtonRequestExt, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; @@ -16,51 +23,37 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmResetRecover { Intro, Menu, } impl FlowState for ConfirmResetRecover { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmResetRecover::Intro, SwipeDirection::Left) => { - Decision::Goto(ConfirmResetRecover::Menu, direction) - } - (ConfirmResetRecover::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmResetRecover::Intro, direction) - } - (ConfirmResetRecover::Intro, SwipeDirection::Up) => { - Decision::Return(FlowMsg::Confirmed) - } - _ => Decision::Nothing, + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::Intro, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ConfirmResetRecover::Intro, FlowMsg::Info) => { - Decision::Goto(ConfirmResetRecover::Menu, SwipeDirection::Left) - } - (ConfirmResetRecover::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmResetRecover::Intro, SwipeDirection::Right) - } - (ConfirmResetRecover::Menu, FlowMsg::Choice(0)) => Decision::Return(FlowMsg::Cancelled), - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::Menu, FlowMsg::Choice(0)) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, - layout::obj::LayoutObj, - model_mercury::component::{PromptScreen, SwipeContent}, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_confirm_reset_recover( n_args: usize, @@ -104,23 +97,9 @@ impl ConfirmResetRecover { FrameMsg::Button(_) => Some(FlowMsg::Cancelled), }); - let content_confirm = Frame::left_aligned( - TR::reset__title_create_wallet.into(), - SwipeContent::new(PromptScreen::new_hold_to_confirm()), - ) - .with_footer(TR::instructions__hold_to_confirm.into(), None) - .with_swipe(SwipeDirection::Down, SwipeSettings::default()) - .map(|msg| match msg { - FrameMsg::Content(()) => Some(FlowMsg::Confirmed), - _ => Some(FlowMsg::Cancelled), - }); - - let store = flow_store() - .add(content_intro)? - .add(content_menu)? - .add(content_confirm)?; - - let res = SwipeFlow::new(ConfirmResetRecover::Intro, store)?; + let res = SwipeFlow::new(&ConfirmResetRecover::Intro)? + .with_page(&ConfirmResetRecover::Intro, content_intro)? + .with_page(&ConfirmResetRecover::Menu, content_menu)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_set_new_pin.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_set_new_pin.rs index d7d459b9e..8d8923ada 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_set_new_pin.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_set_new_pin.rs @@ -1,14 +1,20 @@ use crate::{ error, - micropython::qstr::Qstr, + micropython::{map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, Paragraphs}, ComponentExt, SwipeDirection, }, - flow::{base::Decision, FlowMsg, FlowState, FlowStore}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; @@ -19,7 +25,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum SetNewPin { Intro, Menu, @@ -28,63 +34,37 @@ pub enum SetNewPin { } impl FlowState for SetNewPin { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { - match (self, direction) { - (SetNewPin::Intro, SwipeDirection::Left) => Decision::Goto(SetNewPin::Menu, direction), - (SetNewPin::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed), + #[inline] + fn index(&'static self) -> usize { + *self as usize + } - (SetNewPin::Menu, SwipeDirection::Right) => Decision::Goto(SetNewPin::Intro, direction), - (SetNewPin::CancelPinIntro, SwipeDirection::Up) => { - Decision::Goto(SetNewPin::CancelPinConfirm, direction) - } - (SetNewPin::CancelPinIntro, SwipeDirection::Right) => { - Decision::Goto(SetNewPin::Intro, direction) - } - (SetNewPin::CancelPinConfirm, SwipeDirection::Down) => { - Decision::Goto(SetNewPin::CancelPinIntro, direction) - } - (SetNewPin::CancelPinConfirm, SwipeDirection::Right) => { - Decision::Goto(SetNewPin::Intro, direction) - } - _ => Decision::Nothing, + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { + match (self, direction) { + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Intro, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::CancelPinIntro, SwipeDirection::Up) => Self::CancelPinConfirm.swipe(direction), + (Self::CancelPinIntro, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::CancelPinConfirm, SwipeDirection::Down) => Self::CancelPinIntro.swipe(direction), + (Self::CancelPinConfirm, SwipeDirection::Right) => Self::Intro.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (SetNewPin::Intro, FlowMsg::Info) => { - Decision::Goto(SetNewPin::Menu, SwipeDirection::Left) - } - (SetNewPin::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(SetNewPin::CancelPinIntro, SwipeDirection::Left) - } - (SetNewPin::Menu, FlowMsg::Cancelled) => { - Decision::Goto(SetNewPin::Intro, SwipeDirection::Right) - } - (SetNewPin::CancelPinIntro, FlowMsg::Cancelled) => { - Decision::Goto(SetNewPin::Intro, SwipeDirection::Right) - } - (SetNewPin::CancelPinConfirm, FlowMsg::Cancelled) => { - Decision::Goto(SetNewPin::CancelPinIntro, SwipeDirection::Right) - } - (SetNewPin::CancelPinConfirm, FlowMsg::Confirmed) => { - Decision::Return(FlowMsg::Cancelled) - } - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(0)) => Self::CancelPinIntro.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::CancelPinIntro, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::CancelPinConfirm, FlowMsg::Cancelled) => Self::CancelPinIntro.swipe_right(), + (Self::CancelPinConfirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, - flow::{flow_store, SwipeFlow}, - layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_set_new_pin(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, SetNewPin::new_obj) } @@ -155,12 +135,11 @@ impl SetNewPin { _ => None, }); - let store = flow_store() - .add(content_intro)? - .add(content_menu)? - .add(content_cancel_intro)? - .add(content_cancel_confirm)?; - let res = SwipeFlow::new(SetNewPin::Intro, store)?; + let res = SwipeFlow::new(&SetNewPin::Intro)? + .with_page(&SetNewPin::Intro, content_intro)? + .with_page(&SetNewPin::Menu, content_menu)? + .with_page(&SetNewPin::CancelPinIntro, content_cancel_intro)? + .with_page(&SetNewPin::CancelPinConfirm, content_cancel_confirm)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs b/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs index 55ec755a5..6fee0ae4a 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/confirm_summary.rs @@ -1,23 +1,29 @@ use crate::{ error, - micropython::{iter::IterBuf, qstr::Qstr}, + micropython::{iter::IterBuf, map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ button_request::ButtonRequest, - component::{ButtonRequestExt, ComponentExt, SwipeDirection}, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + component::{swipe_detect::SwipeSettings, ButtonRequestExt, ComponentExt, SwipeDirection}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; -use super::super::{ - component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg}, - theme, +use super::{ + super::{ + component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg}, + theme, + }, + util::ShowInfoParams, }; -use super::util::ShowInfoParams; - -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ConfirmSummary { Summary, Hold, @@ -28,62 +34,40 @@ pub enum ConfirmSummary { } impl FlowState for ConfirmSummary { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ConfirmSummary::Summary | ConfirmSummary::Hold, SwipeDirection::Left) => { - Decision::Goto(ConfirmSummary::Menu, direction) + (Self::Summary | Self::Hold, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Summary, SwipeDirection::Up) => Self::Hold.swipe(direction), + (Self::Hold, SwipeDirection::Down) => Self::Summary.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Summary.swipe(direction), + (Self::Menu, SwipeDirection::Left) => Self::FeeInfo.swipe(direction), + (Self::AccountInfo | Self::FeeInfo | Self::CancelTap, SwipeDirection::Right) => { + Self::Menu.swipe(direction) } - (ConfirmSummary::Summary, SwipeDirection::Up) => { - Decision::Goto(ConfirmSummary::Hold, direction) - } - (ConfirmSummary::Hold, SwipeDirection::Down) => { - Decision::Goto(ConfirmSummary::Summary, direction) - } - (ConfirmSummary::Menu, SwipeDirection::Right) => { - Decision::Goto(ConfirmSummary::Summary, direction) - } - (ConfirmSummary::Menu, SwipeDirection::Left) => { - Decision::Goto(ConfirmSummary::FeeInfo, direction) - } - ( - ConfirmSummary::AccountInfo | ConfirmSummary::FeeInfo | ConfirmSummary::CancelTap, - SwipeDirection::Right, - ) => Decision::Goto(ConfirmSummary::Menu, direction), - _ => Decision::Nothing, + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (_, FlowMsg::Info) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Left), - (ConfirmSummary::Hold, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed), - (ConfirmSummary::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(ConfirmSummary::FeeInfo, SwipeDirection::Left) - } - (ConfirmSummary::Menu, FlowMsg::Choice(1)) => { - Decision::Goto(ConfirmSummary::AccountInfo, SwipeDirection::Left) - } - (ConfirmSummary::Menu, FlowMsg::Choice(2)) => { - Decision::Goto(ConfirmSummary::CancelTap, SwipeDirection::Left) - } - (ConfirmSummary::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ConfirmSummary::Summary, SwipeDirection::Right) - } - (ConfirmSummary::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled), - (_, FlowMsg::Cancelled) => Decision::Goto(ConfirmSummary::Menu, SwipeDirection::Right), - _ => Decision::Nothing, + (_, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Hold, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, FlowMsg::Choice(0)) => Self::FeeInfo.swipe_left(), + (Self::Menu, FlowMsg::Choice(1)) => Self::AccountInfo.swipe_left(), + (Self::Menu, FlowMsg::Choice(2)) => Self::CancelTap.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Summary.swipe_right(), + (Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + (_, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_confirm_summary(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmSummary::new_obj) } @@ -171,14 +155,14 @@ impl ConfirmSummary { FrameMsg::Button(_) => Some(FlowMsg::Cancelled), }); - let store = flow_store() - .add(content_summary)? - .add(content_hold)? - .add(content_menu)? - .add(content_fee)? - .add(content_account)? - .add(content_cancel_tap)?; - let res = SwipeFlow::new(ConfirmSummary::Summary, store)?; + let res = SwipeFlow::new(&ConfirmSummary::Summary)? + .with_page(&ConfirmSummary::Summary, content_summary)? + .with_page(&ConfirmSummary::Hold, content_hold)? + .with_page(&ConfirmSummary::Menu, content_menu)? + .with_page(&ConfirmSummary::FeeInfo, content_fee)? + .with_page(&ConfirmSummary::AccountInfo, content_account)? + .with_page(&ConfirmSummary::CancelTap, content_cancel_tap)?; + Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/get_address.rs b/core/embed/rust/src/ui/model_mercury/flow/get_address.rs index 155521538..723806b8a 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/get_address.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/get_address.rs @@ -1,16 +1,21 @@ use crate::{ error, - micropython::{iter::IterBuf, qstr::Qstr}, + micropython::{iter::IterBuf, map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ button_request::ButtonRequest, component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, ParagraphSource, Paragraphs}, ButtonRequestExt, ComponentExt, Qr, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, - layout::util::ConfirmBlob, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, SwipePage, + }, + layout::{obj::LayoutObj, util::ConfirmBlob}, + model_mercury::component::SwipeContent, }, }; @@ -24,7 +29,7 @@ use super::super::{ const QR_BORDER: i16 = 4; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum GetAddress { Address, Tap, @@ -37,104 +42,48 @@ pub enum GetAddress { } impl FlowState for GetAddress { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (GetAddress::Address, SwipeDirection::Left) => { - Decision::Goto(GetAddress::Menu, direction) - } - (GetAddress::Address, SwipeDirection::Up) => Decision::Goto(GetAddress::Tap, direction), - (GetAddress::Tap, SwipeDirection::Down) => { - Decision::Goto(GetAddress::Address, direction) - } - (GetAddress::Tap, SwipeDirection::Left) => Decision::Goto(GetAddress::Menu, direction), - (GetAddress::Menu, SwipeDirection::Right) => { - Decision::Goto(GetAddress::Address, direction) - } - (GetAddress::QrCode, SwipeDirection::Right) => { - Decision::Goto(GetAddress::Menu, direction) - } - (GetAddress::AccountInfo, SwipeDirection::Right) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Right) - } - (GetAddress::Cancel, SwipeDirection::Up) => { - Decision::Goto(GetAddress::CancelTap, direction) - } - (GetAddress::Cancel, SwipeDirection::Right) => { - Decision::Goto(GetAddress::Menu, direction) - } - (GetAddress::CancelTap, SwipeDirection::Down) => { - Decision::Goto(GetAddress::Cancel, direction) - } - (GetAddress::CancelTap, SwipeDirection::Right) => { - Decision::Goto(GetAddress::Menu, direction) - } - _ => Decision::Nothing, + (Self::Address, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Address, SwipeDirection::Up) => Self::Tap.swipe(direction), + (Self::Tap, SwipeDirection::Down) => Self::Address.swipe(direction), + (Self::Tap, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Address.swipe(direction), + (Self::QrCode, SwipeDirection::Right) => Self::Menu.swipe(direction), + (Self::AccountInfo, SwipeDirection::Right) => Self::Menu.swipe_right(), + (Self::Cancel, SwipeDirection::Up) => Self::CancelTap.swipe(direction), + (Self::Cancel, SwipeDirection::Right) => Self::Menu.swipe(direction), + (Self::CancelTap, SwipeDirection::Down) => Self::Cancel.swipe(direction), + (Self::CancelTap, SwipeDirection::Right) => Self::Menu.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (GetAddress::Address, FlowMsg::Info) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Left) - } - - (GetAddress::Tap, FlowMsg::Confirmed) => { - Decision::Goto(GetAddress::Confirmed, SwipeDirection::Up) - } - - (GetAddress::Tap, FlowMsg::Info) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Left) - } - - (GetAddress::Confirmed, _) => Decision::Return(FlowMsg::Confirmed), - - (GetAddress::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(GetAddress::QrCode, SwipeDirection::Left) - } - - (GetAddress::Menu, FlowMsg::Choice(1)) => { - Decision::Goto(GetAddress::AccountInfo, SwipeDirection::Left) - } - - (GetAddress::Menu, FlowMsg::Choice(2)) => { - Decision::Goto(GetAddress::Cancel, SwipeDirection::Left) - } - - (GetAddress::Menu, FlowMsg::Cancelled) => { - Decision::Goto(GetAddress::Address, SwipeDirection::Right) - } - - (GetAddress::QrCode, FlowMsg::Cancelled) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Right) - } - - (GetAddress::AccountInfo, FlowMsg::Cancelled) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Right) - } - - (GetAddress::Cancel, FlowMsg::Cancelled) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Right) - } - - (GetAddress::CancelTap, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Cancelled), - - (GetAddress::CancelTap, FlowMsg::Cancelled) => { - Decision::Goto(GetAddress::Menu, SwipeDirection::Right) - } - - _ => Decision::Nothing, + (Self::Address, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Tap, FlowMsg::Confirmed) => Self::Confirmed.swipe_up(), + (Self::Tap, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Confirmed, _) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, FlowMsg::Choice(0)) => Self::QrCode.swipe_left(), + (Self::Menu, FlowMsg::Choice(1)) => Self::AccountInfo.swipe_left(), + (Self::Menu, FlowMsg::Choice(2)) => Self::Cancel.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Address.swipe_right(), + (Self::QrCode, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::AccountInfo, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::Cancel, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::CancelTap, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + (Self::CancelTap, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, flow::SwipePage, layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_get_address(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, GetAddress::new_obj) } @@ -271,16 +220,15 @@ impl GetAddress { _ => None, }); - let store = flow_store() - .add(content_address)? - .add(content_tap)? - .add(content_confirmed)? - .add(content_menu)? - .add(content_qr)? - .add(content_account)? - .add(content_cancel_info)? - .add(content_cancel_tap)?; - let res = SwipeFlow::new(GetAddress::Address, store)?; + let res = SwipeFlow::new(&GetAddress::Address)? + .with_page(&GetAddress::Address, content_address)? + .with_page(&GetAddress::Tap, content_tap)? + .with_page(&GetAddress::Confirmed, content_confirmed)? + .with_page(&GetAddress::Menu, content_menu)? + .with_page(&GetAddress::QrCode, content_qr)? + .with_page(&GetAddress::AccountInfo, content_account)? + .with_page(&GetAddress::Cancel, content_cancel_info)? + .with_page(&GetAddress::CancelTap, content_cancel_tap)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/prompt_backup.rs b/core/embed/rust/src/ui/model_mercury/flow/prompt_backup.rs index ba2b43bb9..fdf691199 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/prompt_backup.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/prompt_backup.rs @@ -1,13 +1,20 @@ use crate::{ error, + micropython::{map::Map, obj::Obj, util}, strutil::TString, translations::TR, ui::{ component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, Paragraphs}, ComponentExt, SwipeDirection, }, - flow::{base::Decision, FlowMsg, FlowState, FlowStore}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; @@ -18,7 +25,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum PromptBackup { Intro, Menu, @@ -27,68 +34,39 @@ pub enum PromptBackup { } impl FlowState for PromptBackup { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { - match (self, direction) { - (PromptBackup::Intro, SwipeDirection::Left) => { - Decision::Goto(PromptBackup::Menu, direction) - } - (PromptBackup::Intro, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed), - - (PromptBackup::Menu, SwipeDirection::Right) => { - Decision::Goto(PromptBackup::Intro, direction) - } + #[inline] + fn index(&'static self) -> usize { + *self as usize + } - (PromptBackup::SkipBackupIntro, SwipeDirection::Up) => { - Decision::Goto(PromptBackup::SkipBackupConfirm, direction) - } - (PromptBackup::SkipBackupIntro, SwipeDirection::Right) => { - Decision::Goto(PromptBackup::Intro, direction) - } - (PromptBackup::SkipBackupConfirm, SwipeDirection::Down) => { - Decision::Goto(PromptBackup::SkipBackupIntro, direction) - } - (PromptBackup::SkipBackupConfirm, SwipeDirection::Right) => { - Decision::Goto(PromptBackup::Intro, direction) + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { + match (self, direction) { + (Self::Intro, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Intro, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::SkipBackupIntro, SwipeDirection::Up) => Self::SkipBackupConfirm.swipe(direction), + (Self::SkipBackupIntro, SwipeDirection::Right) => Self::Intro.swipe(direction), + (Self::SkipBackupConfirm, SwipeDirection::Down) => { + Self::SkipBackupIntro.swipe(direction) } - _ => Decision::Nothing, + (Self::SkipBackupConfirm, SwipeDirection::Right) => Self::Intro.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (PromptBackup::Intro, FlowMsg::Info) => { - Decision::Goto(PromptBackup::Menu, SwipeDirection::Left) - } - (PromptBackup::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(PromptBackup::SkipBackupIntro, SwipeDirection::Left) - } - (PromptBackup::Menu, FlowMsg::Cancelled) => { - Decision::Goto(PromptBackup::Intro, SwipeDirection::Right) - } - (PromptBackup::SkipBackupIntro, FlowMsg::Cancelled) => { - Decision::Goto(PromptBackup::Menu, SwipeDirection::Right) - } - (PromptBackup::SkipBackupConfirm, FlowMsg::Cancelled) => { - Decision::Goto(PromptBackup::SkipBackupIntro, SwipeDirection::Right) - } - (PromptBackup::SkipBackupConfirm, FlowMsg::Confirmed) => { - Decision::Return(FlowMsg::Cancelled) - } - _ => Decision::Nothing, + (Self::Intro, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(0)) => Self::SkipBackupIntro.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Intro.swipe_right(), + (Self::SkipBackupIntro, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::SkipBackupConfirm, FlowMsg::Cancelled) => Self::SkipBackupIntro.swipe_right(), + (Self::SkipBackupConfirm, FlowMsg::Confirmed) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, - flow::{flow_store, SwipeFlow}, - layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_prompt_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, PromptBackup::new_obj) } @@ -159,12 +137,11 @@ impl PromptBackup { _ => None, }); - let store = flow_store() - .add(content_intro)? - .add(content_menu)? - .add(content_skip_intro)? - .add(content_skip_confirm)?; - let res = SwipeFlow::new(PromptBackup::Intro, store)?; + let res = SwipeFlow::new(&PromptBackup::Intro)? + .with_page(&PromptBackup::Intro, content_intro)? + .with_page(&PromptBackup::Menu, content_menu)? + .with_page(&PromptBackup::SkipBackupIntro, content_skip_intro)? + .with_page(&PromptBackup::SkipBackupConfirm, content_skip_confirm)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/request_number.rs b/core/embed/rust/src/ui/model_mercury/flow/request_number.rs index 55ffad8a2..4aa70ce32 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/request_number.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/request_number.rs @@ -1,15 +1,21 @@ use crate::{ error, - micropython::qstr::Qstr, + micropython::{map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ button_request::ButtonRequest, component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, Paragraphs}, ButtonRequestExt, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; @@ -21,7 +27,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum RequestNumber { Number, Menu, @@ -29,49 +35,32 @@ pub enum RequestNumber { } impl FlowState for RequestNumber { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (RequestNumber::Number, SwipeDirection::Left) => { - Decision::Goto(RequestNumber::Menu, direction) - } - (RequestNumber::Menu, SwipeDirection::Right) => { - Decision::Goto(RequestNumber::Number, direction) - } - (RequestNumber::Info, SwipeDirection::Right) => { - Decision::Goto(RequestNumber::Menu, direction) - } - _ => Decision::Nothing, + (Self::Number, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Number.swipe(direction), + (Self::Info, SwipeDirection::Right) => Self::Menu.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (RequestNumber::Number, FlowMsg::Info) => { - Decision::Goto(RequestNumber::Menu, SwipeDirection::Left) - } - (RequestNumber::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(RequestNumber::Info, SwipeDirection::Left) - } - (RequestNumber::Menu, FlowMsg::Cancelled) => { - Decision::Goto(RequestNumber::Number, SwipeDirection::Right) - } - (RequestNumber::Info, FlowMsg::Cancelled) => { - Decision::Goto(RequestNumber::Menu, SwipeDirection::Right) - } - (RequestNumber::Number, FlowMsg::Choice(n)) => Decision::Return(FlowMsg::Choice(n)), - _ => Decision::Nothing, + (Self::Number, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(0)) => Self::Info.swipe_left(), + (Self::Menu, FlowMsg::Cancelled) => Self::Number.swipe_right(), + (Self::Info, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::Number, FlowMsg::Choice(n)) => self.return_msg(FlowMsg::Choice(n)), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, RequestNumber::new_obj) } @@ -143,11 +132,10 @@ impl RequestNumber { _ => None, }); - let store = flow_store() - .add(content_number_input)? - .add(content_menu)? - .add(content_info)?; - let res = SwipeFlow::new(RequestNumber::Number, store)?; + let res = SwipeFlow::new(&RequestNumber::Number)? + .with_page(&RequestNumber::Number, content_number_input)? + .with_page(&RequestNumber::Menu, content_menu)? + .with_page(&RequestNumber::Info, content_info)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs b/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs index 920945008..bae28e050 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/show_share_words.rs @@ -10,7 +10,10 @@ use crate::{ text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt}, ButtonRequestExt, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, layout::obj::LayoutObj, model_mercury::component::SwipeContent, }, @@ -22,7 +25,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ShowShareWords { Instruction, Words, @@ -31,39 +34,28 @@ pub enum ShowShareWords { } impl FlowState for ShowShareWords { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ShowShareWords::Instruction, SwipeDirection::Up) => { - Decision::Goto(ShowShareWords::Words, direction) - } - (ShowShareWords::Confirm, SwipeDirection::Down) => { - Decision::Goto(ShowShareWords::Words, direction) - } - (ShowShareWords::Words, SwipeDirection::Up) => { - Decision::Goto(ShowShareWords::Confirm, direction) - } - (ShowShareWords::Words, SwipeDirection::Down) => { - Decision::Goto(ShowShareWords::Instruction, direction) - } - (ShowShareWords::CheckBackupIntro, SwipeDirection::Up) => { - Decision::Return(FlowMsg::Confirmed) - } - _ => Decision::Nothing, + (Self::Instruction, SwipeDirection::Up) => Self::Words.swipe(direction), + (Self::Confirm, SwipeDirection::Down) => Self::Words.swipe(direction), + (Self::Words, SwipeDirection::Up) => Self::Confirm.swipe(direction), + (Self::Words, SwipeDirection::Down) => Self::Instruction.swipe(direction), + (Self::CheckBackupIntro, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ShowShareWords::Words, FlowMsg::Cancelled) => { - Decision::Goto(ShowShareWords::Instruction, SwipeDirection::Down) - } - (ShowShareWords::Words, FlowMsg::Confirmed) => { - Decision::Goto(ShowShareWords::Confirm, SwipeDirection::Up) - } - (ShowShareWords::Confirm, FlowMsg::Confirmed) => { - Decision::Goto(ShowShareWords::CheckBackupIntro, SwipeDirection::Up) - } - _ => Decision::Nothing, + (Self::Words, FlowMsg::Cancelled) => Self::Instruction.swipe_down(), + (Self::Words, FlowMsg::Confirmed) => Self::Confirm.swipe_up(), + (Self::Confirm, FlowMsg::Confirmed) => Self::CheckBackupIntro.swipe_up(), + _ => self.do_nothing(), } } } @@ -131,12 +123,14 @@ impl ShowShareWords { .with_swipe(SwipeDirection::Up, SwipeSettings::default()) .map(|_| Some(FlowMsg::Confirmed)); - let store = flow_store() - .add(content_instruction)? - .add(content_words)? - .add(content_confirm)? - .add(content_check_backup_intro)?; - let res = SwipeFlow::new(ShowShareWords::Instruction, store)?; + let res = SwipeFlow::new(&ShowShareWords::Instruction)? + .with_page(&ShowShareWords::Instruction, content_instruction)? + .with_page(&ShowShareWords::Words, content_words)? + .with_page(&ShowShareWords::Confirm, content_confirm)? + .with_page( + &ShowShareWords::CheckBackupIntro, + content_check_backup_intro, + )?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/show_tutorial.rs b/core/embed/rust/src/ui/model_mercury/flow/show_tutorial.rs index 79fdff291..bd453b37d 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/show_tutorial.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/show_tutorial.rs @@ -1,5 +1,6 @@ use crate::{ error, + micropython::{map::Map, obj::Obj, util}, translations::TR, ui::{ component::{ @@ -7,7 +8,10 @@ use crate::{ text::paragraphs::{Paragraph, Paragraphs}, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, layout::obj::LayoutObj, model_mercury::component::SwipeContent, }, @@ -18,7 +22,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum ShowTutorial { StepWelcome, StepBegin, @@ -32,76 +36,43 @@ pub enum ShowTutorial { } impl FlowState for ShowTutorial { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (ShowTutorial::StepBegin, SwipeDirection::Up) => { - Decision::Goto(ShowTutorial::StepNavigation, direction) - } - (ShowTutorial::StepNavigation, SwipeDirection::Up) => { - Decision::Goto(ShowTutorial::StepMenu, direction) - } - (ShowTutorial::StepNavigation, SwipeDirection::Down) => { - Decision::Goto(ShowTutorial::StepBegin, direction) - } - (ShowTutorial::StepMenu, SwipeDirection::Up) => { - Decision::Goto(ShowTutorial::StepHold, direction) - } - (ShowTutorial::StepMenu, SwipeDirection::Down) => { - Decision::Goto(ShowTutorial::StepNavigation, direction) - } - (ShowTutorial::StepMenu, SwipeDirection::Left) => { - Decision::Goto(ShowTutorial::Menu, direction) - } - (ShowTutorial::Menu, SwipeDirection::Left) => { - Decision::Goto(ShowTutorial::DidYouKnow, direction) - } - (ShowTutorial::Menu, SwipeDirection::Right) => { - Decision::Goto(ShowTutorial::StepBegin, direction) - } - (ShowTutorial::DidYouKnow, SwipeDirection::Right) => { - Decision::Goto(ShowTutorial::Menu, direction) - } - (ShowTutorial::StepDone, SwipeDirection::Up) => Decision::Return(FlowMsg::Confirmed), - _ => Decision::Nothing, + (Self::StepBegin, SwipeDirection::Up) => Self::StepNavigation.swipe(direction), + (Self::StepNavigation, SwipeDirection::Up) => Self::StepMenu.swipe(direction), + (Self::StepNavigation, SwipeDirection::Down) => Self::StepBegin.swipe(direction), + (Self::StepMenu, SwipeDirection::Up) => Self::StepHold.swipe(direction), + (Self::StepMenu, SwipeDirection::Down) => Self::StepNavigation.swipe(direction), + (Self::StepMenu, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Menu, SwipeDirection::Left) => Self::DidYouKnow.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::StepBegin.swipe(direction), + (Self::DidYouKnow, SwipeDirection::Right) => Self::Menu.swipe(direction), + (Self::StepDone, SwipeDirection::Up) => self.return_msg(FlowMsg::Confirmed), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (ShowTutorial::StepWelcome, FlowMsg::Confirmed) => { - Decision::Goto(ShowTutorial::StepBegin, SwipeDirection::Up) - } - (ShowTutorial::StepMenu, FlowMsg::Info) => { - Decision::Goto(ShowTutorial::Menu, SwipeDirection::Left) - } - (ShowTutorial::Menu, FlowMsg::Choice(0)) => { - Decision::Goto(ShowTutorial::DidYouKnow, SwipeDirection::Left) - } - (ShowTutorial::Menu, FlowMsg::Choice(1)) => { - Decision::Goto(ShowTutorial::StepBegin, SwipeDirection::Right) - } - (ShowTutorial::Menu, FlowMsg::Choice(2)) => { - Decision::Goto(ShowTutorial::HoldToExit, SwipeDirection::Up) - } - (ShowTutorial::Menu, FlowMsg::Cancelled) => { - Decision::Goto(ShowTutorial::StepMenu, SwipeDirection::Right) - } - (ShowTutorial::DidYouKnow, FlowMsg::Cancelled) => { - Decision::Goto(ShowTutorial::Menu, SwipeDirection::Right) - } - (ShowTutorial::StepHold, FlowMsg::Confirmed) => { - Decision::Goto(ShowTutorial::StepDone, SwipeDirection::Up) - } - (ShowTutorial::HoldToExit, FlowMsg::Confirmed) => { - Decision::Goto(ShowTutorial::StepDone, SwipeDirection::Up) - } - _ => Decision::Nothing, + (Self::StepWelcome, FlowMsg::Confirmed) => Self::StepBegin.swipe_up(), + (Self::StepMenu, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(0)) => Self::DidYouKnow.swipe_left(), + (Self::Menu, FlowMsg::Choice(1)) => Self::StepBegin.swipe_right(), + (Self::Menu, FlowMsg::Choice(2)) => Self::HoldToExit.swipe_up(), + (Self::Menu, FlowMsg::Cancelled) => Self::StepMenu.swipe_right(), + (Self::DidYouKnow, FlowMsg::Cancelled) => Self::Menu.swipe_right(), + (Self::StepHold, FlowMsg::Confirmed) => Self::StepDone.swipe_up(), + (Self::HoldToExit, FlowMsg::Confirmed) => Self::StepDone.swipe_up(), + _ => self.do_nothing(), } } } -use crate::micropython::{map::Map, obj::Obj, util}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_show_tutorial(_n_args: usize, _args: *const Obj, _kwargs: *mut Map) -> Obj { unsafe { util::try_or_raise(ShowTutorial::new_obj) } @@ -213,17 +184,16 @@ impl ShowTutorial { .with_footer(TR::instructions__exit_tutorial.into(), None) .map(|msg| matches!(msg, FrameMsg::Content(())).then_some(FlowMsg::Confirmed)); - let store = flow_store() - .add(content_step_welcome)? - .add(content_step_begin)? - .add(content_step_navigation)? - .add(content_step_menu)? - .add(content_step_hold)? - .add(content_step_done)? - .add(content_menu)? - .add(content_did_you_know)? - .add(content_hold_to_exit)?; - let res = SwipeFlow::new(ShowTutorial::StepWelcome, store)?; + let res = SwipeFlow::new(&ShowTutorial::StepWelcome)? + .with_page(&ShowTutorial::StepWelcome, content_step_welcome)? + .with_page(&ShowTutorial::StepBegin, content_step_begin)? + .with_page(&ShowTutorial::StepNavigation, content_step_navigation)? + .with_page(&ShowTutorial::StepMenu, content_step_menu)? + .with_page(&ShowTutorial::StepHold, content_step_hold)? + .with_page(&ShowTutorial::StepDone, content_step_done)? + .with_page(&ShowTutorial::Menu, content_menu)? + .with_page(&ShowTutorial::DidYouKnow, content_did_you_know)? + .with_page(&ShowTutorial::HoldToExit, content_hold_to_exit)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs b/core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs index acee0c6b2..c4bf1cbe9 100644 --- a/core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs +++ b/core/embed/rust/src/ui/model_mercury/flow/warning_hi_prio.rs @@ -1,14 +1,20 @@ use crate::{ error, - micropython::qstr::Qstr, + micropython::{map::Map, obj::Obj, qstr::Qstr, util}, strutil::TString, translations::TR, ui::{ component::{ + swipe_detect::SwipeSettings, text::paragraphs::{Paragraph, ParagraphSource}, ComponentExt, SwipeDirection, }, - flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow}, + flow::{ + base::{DecisionBuilder as _, StateChange}, + FlowMsg, FlowState, SwipeFlow, + }, + layout::obj::LayoutObj, + model_mercury::component::SwipeContent, }, }; @@ -17,7 +23,7 @@ use super::super::{ theme, }; -#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum WarningHiPrio { Message, Menu, @@ -25,47 +31,32 @@ pub enum WarningHiPrio { } impl FlowState for WarningHiPrio { - fn handle_swipe(&self, direction: SwipeDirection) -> Decision { + #[inline] + fn index(&'static self) -> usize { + *self as usize + } + + fn handle_swipe(&'static self, direction: SwipeDirection) -> StateChange { match (self, direction) { - (WarningHiPrio::Message, SwipeDirection::Left) => { - Decision::Goto(WarningHiPrio::Menu, direction) - } - (WarningHiPrio::Message, SwipeDirection::Up) => { - Decision::Goto(WarningHiPrio::Cancelled, direction) - } - (WarningHiPrio::Menu, SwipeDirection::Right) => { - Decision::Goto(WarningHiPrio::Message, direction) - } - _ => Decision::Nothing, + (Self::Message, SwipeDirection::Left) => Self::Menu.swipe(direction), + (Self::Message, SwipeDirection::Up) => Self::Cancelled.swipe(direction), + (Self::Menu, SwipeDirection::Right) => Self::Message.swipe(direction), + _ => self.do_nothing(), } } - fn handle_event(&self, msg: FlowMsg) -> Decision { + fn handle_event(&'static self, msg: FlowMsg) -> StateChange { match (self, msg) { - (WarningHiPrio::Message, FlowMsg::Info) => { - Decision::Goto(WarningHiPrio::Menu, SwipeDirection::Left) - } - (WarningHiPrio::Menu, FlowMsg::Choice(1)) => Decision::Return(FlowMsg::Confirmed), - (WarningHiPrio::Menu, FlowMsg::Choice(_)) => { - Decision::Goto(WarningHiPrio::Cancelled, SwipeDirection::Up) - } - (WarningHiPrio::Menu, FlowMsg::Cancelled) => { - Decision::Goto(WarningHiPrio::Message, SwipeDirection::Right) - } - (WarningHiPrio::Cancelled, _) => Decision::Return(FlowMsg::Cancelled), - _ => Decision::Nothing, + (Self::Message, FlowMsg::Info) => Self::Menu.swipe_left(), + (Self::Menu, FlowMsg::Choice(1)) => self.return_msg(FlowMsg::Confirmed), + (Self::Menu, FlowMsg::Choice(_)) => Self::Cancelled.swipe_up(), + (Self::Menu, FlowMsg::Cancelled) => Self::Message.swipe_right(), + (Self::Cancelled, _) => self.return_msg(FlowMsg::Cancelled), + _ => self.do_nothing(), } } } -use crate::{ - micropython::{map::Map, obj::Obj, util}, - ui::{ - component::swipe_detect::SwipeSettings, layout::obj::LayoutObj, - model_mercury::component::SwipeContent, - }, -}; - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub extern "C" fn new_warning_hi_prio(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, WarningHiPrio::new_obj) } @@ -118,11 +109,10 @@ impl WarningHiPrio { .with_footer(TR::instructions__continue_in_app.into(), None) .map(|_| Some(FlowMsg::Cancelled)); - let store = flow_store() - .add(content_message)? - .add(content_menu)? - .add(content_cancelled)?; - let res = SwipeFlow::new(WarningHiPrio::Message, store)?; + let res = SwipeFlow::new(&WarningHiPrio::Message)? + .with_page(&WarningHiPrio::Message, content_message)? + .with_page(&WarningHiPrio::Menu, content_menu)? + .with_page(&WarningHiPrio::Cancelled, content_cancelled)?; Ok(LayoutObj::new(res)?.into()) } } diff --git a/core/embed/rust/src/ui/shape/mod.rs b/core/embed/rust/src/ui/shape/mod.rs index 7b0bf52e1..7497ee224 100644 --- a/core/embed/rust/src/ui/shape/mod.rs +++ b/core/embed/rust/src/ui/shape/mod.rs @@ -28,12 +28,12 @@ pub use canvas::{ }; pub use circle::Circle; pub use corner_highlight::CornerHighlight; -pub use display::{render_on_canvas, render_on_display, unlock_bumps_on_failure}; +pub use display::{render_on_canvas, render_on_display, unlock_bumps_on_failure, ConcreteRenderer}; #[cfg(feature = "ui_jpeg_decoder")] pub use jpeg::JpegImage; pub use qrcode::QrImage; pub use rawimage::RawImage; -pub use render::{DirectRenderer, ProgressiveRenderer, Renderer}; +pub use render::{DirectRenderer, ProgressiveRenderer, Renderer, ScopedRenderer}; pub use text::Text; pub use toif::ToifImage; #[cfg(feature = "ui_image_buffer")]