refactor(core/rust): refactor SwipeFlow

* remove SwipeStore in favor of Vec<dyn FlowComponent>
* unify state and controllers
* implement tighter coupling between controller/states and pages of the
flow
pull/4018/head
matejcik 4 months ago committed by matejcik
parent 4c10a4f643
commit 3fcb0acaff

@ -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<Q> {
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<ButtonRequest> here
Goto(Q, SwipeDirection),
Transition(SwipeDirection),
/// Yield a message to the caller of the flow (i.e. micropython), end event
/// processing.
Return(FlowMsg),
}
impl<Q> Decision<Q> {
impl Decision {
pub fn or_else(self, func: impl FnOnce() -> Self) -> Self {
match self {
Decision::Nothing => func(),
@ -44,24 +43,71 @@ impl<Q> Decision<Q> {
}
}
/// 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<Self>;
///
/// 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<Self>;
///
/// 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<T: FlowState> DecisionBuilder for T {}

@ -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;

@ -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<Gc<dyn Component + SimpleSwipable>>` 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<FlowMsg>;
/// 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<T>(&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<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
self,
elem: E,
) -> Result<impl FlowStore, error::Error>
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<FlowMsg> {
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<T>(&mut self, _i: usize, _func: impl FnOnce(&mut dyn Swipable) -> T) -> T {
panic!()
}
fn add<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
self,
elem: E,
) -> Result<impl FlowStore, error::Error>
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<E: Component<Msg = FlowMsg>, P> {
/// Component allocated on micropython heap.
pub elem: Gc<E>,
/// Nested FlowStore.
pub next: P,
}
impl<E: Component<Msg = FlowMsg>, P> FlowComponent2<E, P> {
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<E, P> FlowStore for FlowComponent2<E, P>
where
E: Component<Msg = FlowMsg> + 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<FlowMsg> {
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<T>(&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<F: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
self,
elem: F,
) -> Result<impl FlowStore, error::Error>
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())
}
}

@ -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<FlowMsg>;
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<Msg = FlowMsg> + MaybeTrace + Swipable,
R: Renderer<'s>,
{
fn place(&mut self, bounds: Rect) -> Rect {
<Self as Component>::place(self, bounds)
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<FlowMsg> {
<Self as Component>::event(self, ctx, event)
}
fn render(&'s self, target: &mut R) {
<Self as Component>::render(self, target)
}
#[cfg(feature = "ui_debug")]
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
<Self as crate::trace::Trace>::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<T> 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<Q, S> {
/// 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<GcBox<dyn FlowComponentDynTrait>, 10>,
/// Swipe detector.
swipe: SwipeDetect,
/// Swipe allowed
@ -34,33 +108,53 @@ pub struct SwipeFlow<Q, S> {
internal_pages: u16,
/// If triggering swipe by event, make this decision instead of default
/// after the swipe.
decision_override: Option<Decision<Q>>,
decision_override: Option<StateChange>,
}
impl<Q: FlowState, S: FlowStore> SwipeFlow<Q, S> {
pub fn new(init: Q, store: S) -> Result<Self, error::Error> {
impl SwipeFlow {
pub fn new(initial_state: &'static dyn FlowState) -> Result<Self, error::Error> {
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<Self, error::Error> {
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<dyn FlowComponentDynTrait> {
&self.store[self.state.index()]
}
fn current_page_mut(&mut self) -> &mut GcBox<dyn FlowComponentDynTrait> {
&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<Q: FlowState, S: FlowStore> SwipeFlow<Q, S> {
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<Q> {
) -> StateChange {
self.state.handle_swipe(direction)
}
fn handle_event_child(&mut self, ctx: &mut EventCtx, event: Event) -> Decision<Q> {
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<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
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<Self::Msg> {
let mut decision: Decision<Q> = Decision::Nothing;
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<FlowMsg> {
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<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
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<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
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<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
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<Q: FlowState, S: FlowStore> Component for SwipeFlow<Q, S> {
_ => 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<Q: FlowState, S: FlowStore> crate::trace::Trace for SwipeFlow<Q, S> {
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<Q: FlowState, S: FlowStore> crate::ui::layout::obj::ComponentMsgObj for SwipeFlow<Q, S> {
fn msg_try_into_obj(
&self,
msg: Self::Msg,
) -> Result<crate::micropython::obj::Obj, error::Error> {
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<Obj, Error> {
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)
}
}

@ -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<Gc<Self>, Error> {
pub fn new(root: impl ObjComponent + 'static) -> Result<Gc<Self>, Error> {
// SAFETY: This is a Python object and hase a base as first element
unsafe {
Gc::new_with_custom_finaliser(Self {

@ -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<Self> {
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<Self> {
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<Self> {
#[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<Self> {
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<T: Component + MaybeTrace + 'static>(
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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<Self> {
#[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<Self> {
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())
}
}

@ -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")]

Loading…
Cancel
Save