You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/rust/src/ui/flow/base.rs

96 lines
3.0 KiB

use crate::ui::{button_request::ButtonRequest, component::EventCtx, geometry::Offset};
use num_traits::ToPrimitive;
#[derive(Copy, Clone)]
pub enum SwipeDirection {
Up,
Down,
Left,
Right,
}
impl SwipeDirection {
pub fn as_offset(self, size: Offset) -> Offset {
match self {
SwipeDirection::Up => Offset::y(-size.y),
SwipeDirection::Down => Offset::y(size.y),
SwipeDirection::Left => Offset::x(-size.x),
SwipeDirection::Right => Offset::x(size.x),
}
}
}
/// Component must implement this trait in order to be part of swipe-based flow.
/// The process of receiving a swipe is two-step, because in order to render the
/// transition animation Flow makes a copy of the pre-swipe state of the
/// component to render it along with the post-swipe state.
pub trait Swipable {
/// Return true if component can handle swipe in a given direction.
fn can_swipe(&self, _direction: SwipeDirection) -> bool {
false
}
/// Make component react to swipe event. Only called if component returned
/// true in the previous function.
fn swiped(&mut self, _ctx: &mut EventCtx, _direction: SwipeDirection) {}
}
/// Component::Msg for component parts of a flow. Converting results of
/// different screens to a shared type makes things easier to work with.
///
/// Also currently the type for message emitted by Flow::event to
/// micropython. They don't need to be the same.
#[derive(Copy, Clone)]
pub enum FlowMsg {
Confirmed,
Cancelled,
Info,
Choice(usize),
}
/// Composable event handler result.
#[derive(Copy, Clone)]
pub enum Decision<Q> {
/// Do nothing, continue with processing next handler.
Nothing,
/// Initiate transition to another state, end event processing.
/// Optionally send button request when transition finishes.
Goto(Q, SwipeDirection, Option<ButtonRequest>),
/// Yield a message to the caller of the flow (i.e. micropython), end event
/// processing.
Return(FlowMsg),
}
impl<Q> Decision<Q> {
pub fn or_else(self, func: impl FnOnce() -> Self) -> Self {
match self {
Decision::Nothing => func(),
_ => self,
}
}
}
/// 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())
}
/// 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>;
/// What to do when the current component emits a message in response to an
/// event.
fn handle_event(&self, msg: FlowMsg) -> Decision<Self>;
}