mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-08 14:31:06 +00:00
fix(core/ui): allow component to terminate flow on swipe
[skip_ci]
This commit is contained in:
parent
cbcd9dd8bd
commit
9c14cae656
@ -65,15 +65,15 @@ pub trait ButtonRequestExt {
|
||||
impl<T: Component> ButtonRequestExt for T {}
|
||||
|
||||
#[cfg(all(feature = "micropython", feature = "touch", feature = "new_rendering"))]
|
||||
impl<T> crate::ui::flow::Swipable for OneButtonRequest<T>
|
||||
impl<T> crate::ui::flow::Swipable<T::Msg> for OneButtonRequest<T>
|
||||
where
|
||||
T: Component + crate::ui::flow::Swipable,
|
||||
T: Component + crate::ui::flow::Swipable<T::Msg>,
|
||||
{
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: crate::ui::component::SwipeDirection,
|
||||
) -> bool {
|
||||
) -> crate::ui::flow::SwipableResult<T::Msg> {
|
||||
self.inner.swipe_start(ctx, direction)
|
||||
}
|
||||
|
||||
|
@ -52,12 +52,17 @@ where
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "micropython", feature = "touch", feature = "new_rendering"))]
|
||||
impl<T, F> crate::ui::flow::Swipable for MsgMap<T, F>
|
||||
impl<T, F, U> crate::ui::flow::Swipable<U> for MsgMap<T, F>
|
||||
where
|
||||
T: Component + crate::ui::flow::Swipable,
|
||||
T: Component + crate::ui::flow::Swipable<T::Msg>,
|
||||
F: Fn(T::Msg) -> Option<U>,
|
||||
{
|
||||
fn swipe_start(&mut self, ctx: &mut EventCtx, direction: super::SwipeDirection) -> bool {
|
||||
self.inner.swipe_start(ctx, direction)
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: super::SwipeDirection,
|
||||
) -> crate::ui::flow::SwipableResult<U> {
|
||||
self.inner.swipe_start(ctx, direction).map(&self.func)
|
||||
}
|
||||
|
||||
fn swipe_finished(&self) -> bool {
|
||||
@ -118,11 +123,15 @@ where
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "micropython", feature = "touch", feature = "new_rendering"))]
|
||||
impl<T, F> crate::ui::flow::Swipable for PageMap<T, F>
|
||||
impl<T, F> crate::ui::flow::Swipable<T::Msg> for PageMap<T, F>
|
||||
where
|
||||
T: Component + crate::ui::flow::Swipable,
|
||||
T: Component + crate::ui::flow::Swipable<T::Msg>,
|
||||
{
|
||||
fn swipe_start(&mut self, ctx: &mut EventCtx, direction: super::SwipeDirection) -> bool {
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: super::SwipeDirection,
|
||||
) -> crate::ui::flow::SwipableResult<T::Msg> {
|
||||
self.inner.swipe_start(ctx, direction)
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,18 @@ use num_traits::ToPrimitive;
|
||||
/// Component must implement this trait in order to be part of swipe-based flow.
|
||||
///
|
||||
/// Default implementation ignores every swipe.
|
||||
pub trait Swipable {
|
||||
/// Attempt a swipe. Return false if component in its current state doesn't
|
||||
/// accept a swipe in the given direction. Start a transition animation
|
||||
/// if true is returned.
|
||||
fn swipe_start(&mut self, _ctx: &mut EventCtx, _direction: SwipeDirection) -> bool {
|
||||
false
|
||||
pub trait Swipable<T> {
|
||||
/// Attempt a swipe. Return `Ignored` if the component in its current state
|
||||
/// doesn't accept a swipe in that direction. Return `Animating` if
|
||||
/// component accepted the swipe and started a transition animation. The
|
||||
/// `Return(x)` variant indicates that the current flow should be terminated
|
||||
/// with the result `x`.
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
_ctx: &mut EventCtx,
|
||||
_direction: SwipeDirection,
|
||||
) -> SwipableResult<T> {
|
||||
SwipableResult::Ignored
|
||||
}
|
||||
|
||||
/// Return true when transition animation is finished. SwipeFlow needs to
|
||||
@ -19,6 +25,28 @@ pub trait Swipable {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SwipableResult<T> {
|
||||
Ignored,
|
||||
Animating,
|
||||
Return(T),
|
||||
}
|
||||
|
||||
impl<T> SwipableResult<T> {
|
||||
pub fn map<U>(self, func: impl FnOnce(T) -> Option<U>) -> SwipableResult<U> {
|
||||
match self {
|
||||
SwipableResult::Ignored => SwipableResult::Ignored,
|
||||
SwipableResult::Animating => SwipableResult::Animating,
|
||||
SwipableResult::Return(x) => {
|
||||
if let Some(res) = func(x) {
|
||||
SwipableResult::Return(res)
|
||||
} else {
|
||||
SwipableResult::Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Component::Msg for component parts of a flow. Converting results of
|
||||
/// different screens to a shared type makes things easier to work with.
|
||||
///
|
||||
|
@ -3,7 +3,7 @@ pub mod page;
|
||||
mod store;
|
||||
mod swipe;
|
||||
|
||||
pub use base::{FlowMsg, FlowState, Swipable};
|
||||
pub use base::{FlowMsg, FlowState, Swipable, SwipableResult};
|
||||
pub use page::{IgnoreSwipe, SwipePage};
|
||||
pub use store::{flow_store, FlowStore};
|
||||
pub use swipe::SwipeFlow;
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
ui::{
|
||||
animation::Animation,
|
||||
component::{Component, Event, EventCtx, Paginate, SwipeDirection},
|
||||
flow::base::Swipable,
|
||||
flow::base::{Swipable, SwipableResult},
|
||||
geometry::{Axis, Rect},
|
||||
shape::Renderer,
|
||||
util,
|
||||
@ -117,17 +117,27 @@ impl<T: Component + Paginate + Clone> Component for SwipePage<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Component + Paginate + Clone> Swipable for SwipePage<T> {
|
||||
fn swipe_start(&mut self, ctx: &mut EventCtx, direction: SwipeDirection) -> bool {
|
||||
impl<T: Component + Paginate + Clone> Swipable<T::Msg> for SwipePage<T> {
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: SwipeDirection,
|
||||
) -> SwipableResult<T::Msg> {
|
||||
match (self.axis, direction) {
|
||||
// Wrong direction
|
||||
(Axis::Horizontal, SwipeDirection::Up | SwipeDirection::Down) => return false,
|
||||
(Axis::Vertical, SwipeDirection::Left | SwipeDirection::Right) => return false,
|
||||
(Axis::Horizontal, SwipeDirection::Up | SwipeDirection::Down) => {
|
||||
return SwipableResult::Ignored
|
||||
}
|
||||
(Axis::Vertical, SwipeDirection::Left | SwipeDirection::Right) => {
|
||||
return SwipableResult::Ignored
|
||||
}
|
||||
// Begin
|
||||
(_, SwipeDirection::Right | SwipeDirection::Down) if self.current == 0 => return false,
|
||||
(_, SwipeDirection::Right | SwipeDirection::Down) if self.current == 0 => {
|
||||
return SwipableResult::Ignored
|
||||
}
|
||||
// End
|
||||
(_, SwipeDirection::Left | SwipeDirection::Up) if self.current + 1 >= self.pages => {
|
||||
return false
|
||||
return SwipableResult::Ignored;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@ -138,7 +148,7 @@ impl<T: Component + Paginate + Clone> Swipable for SwipePage<T> {
|
||||
if util::animation_disabled() {
|
||||
self.inner.change_page(self.current);
|
||||
ctx.request_paint();
|
||||
return true;
|
||||
return SwipableResult::Animating;
|
||||
}
|
||||
self.transition = Some(Transition {
|
||||
cloned: unwrap!(Gc::new(self.inner.clone())),
|
||||
@ -148,7 +158,7 @@ impl<T: Component + Paginate + Clone> Swipable for SwipePage<T> {
|
||||
self.inner.change_page(self.current);
|
||||
ctx.request_anim_frame();
|
||||
ctx.request_paint();
|
||||
true
|
||||
SwipableResult::Animating
|
||||
}
|
||||
|
||||
fn swipe_finished(&self) -> bool {
|
||||
@ -195,7 +205,7 @@ impl<T: Component> Component for IgnoreSwipe<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Swipable for IgnoreSwipe<T> {}
|
||||
impl<T: Component> Swipable<T::Msg> for IgnoreSwipe<T> {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for IgnoreSwipe<T>
|
||||
|
@ -29,10 +29,14 @@ pub trait FlowStore {
|
||||
fn trace(&self, i: usize, t: &mut dyn crate::trace::Tracer);
|
||||
|
||||
/// Forward `Swipable` methods to i-th element.
|
||||
fn map_swipable<T>(&mut self, i: usize, func: impl FnOnce(&mut dyn Swipable) -> T) -> T;
|
||||
fn map_swipable<T>(
|
||||
&mut self,
|
||||
i: usize,
|
||||
func: impl FnOnce(&mut dyn Swipable<FlowMsg>) -> T,
|
||||
) -> T;
|
||||
|
||||
/// Add a Component to the end of a `FlowStore`.
|
||||
fn add<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
|
||||
fn add<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable<FlowMsg>>(
|
||||
self,
|
||||
elem: E,
|
||||
) -> Result<impl FlowStore, error::Error>
|
||||
@ -67,11 +71,15 @@ impl FlowStore for FlowEmpty {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn map_swipable<T>(&mut self, _i: usize, _func: impl FnOnce(&mut dyn Swipable) -> T) -> T {
|
||||
fn map_swipable<T>(
|
||||
&mut self,
|
||||
_i: usize,
|
||||
_func: impl FnOnce(&mut dyn Swipable<FlowMsg>) -> T,
|
||||
) -> T {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn add<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
|
||||
fn add<E: Component<Msg = FlowMsg> + MaybeTrace + Swipable<FlowMsg>>(
|
||||
self,
|
||||
elem: E,
|
||||
) -> Result<impl FlowStore, error::Error>
|
||||
@ -107,7 +115,7 @@ impl<E: Component<Msg = FlowMsg>, P> FlowComponent<E, P> {
|
||||
|
||||
impl<E, P> FlowStore for FlowComponent<E, P>
|
||||
where
|
||||
E: Component<Msg = FlowMsg> + MaybeTrace + Swipable,
|
||||
E: Component<Msg = FlowMsg> + MaybeTrace + Swipable<FlowMsg>,
|
||||
P: FlowStore,
|
||||
{
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -141,7 +149,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn map_swipable<T>(&mut self, i: usize, func: impl FnOnce(&mut dyn Swipable) -> T) -> T {
|
||||
fn map_swipable<T>(
|
||||
&mut self,
|
||||
i: usize,
|
||||
func: impl FnOnce(&mut dyn Swipable<FlowMsg>) -> T,
|
||||
) -> T {
|
||||
if i == 0 {
|
||||
func(self.as_mut())
|
||||
} else {
|
||||
@ -149,7 +161,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn add<F: Component<Msg = FlowMsg> + MaybeTrace + Swipable>(
|
||||
fn add<F: Component<Msg = FlowMsg> + MaybeTrace + Swipable<FlowMsg>>(
|
||||
self,
|
||||
elem: F,
|
||||
) -> Result<impl FlowStore, error::Error>
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
ui::{
|
||||
animation::Animation,
|
||||
component::{Component, Event, EventCtx, Swipe, SwipeDirection},
|
||||
flow::{base::Decision, FlowMsg, FlowState, FlowStore},
|
||||
flow::{base::Decision, FlowMsg, FlowState, FlowStore, SwipableResult},
|
||||
geometry::Rect,
|
||||
shape::Renderer,
|
||||
util,
|
||||
@ -135,13 +135,13 @@ impl<Q: FlowState, S: FlowStore> SwipeFlow<Q, S> {
|
||||
|
||||
fn handle_swipe_child(&mut self, ctx: &mut EventCtx, direction: SwipeDirection) -> Decision<Q> {
|
||||
let i = self.state.index();
|
||||
if self
|
||||
match self
|
||||
.store
|
||||
.map_swipable(i, |s| s.swipe_start(ctx, direction))
|
||||
{
|
||||
Decision::Goto(self.state, direction)
|
||||
} else {
|
||||
Decision::Nothing
|
||||
SwipableResult::Ignored => Decision::Nothing,
|
||||
SwipableResult::Animating => Decision::Goto(self.state, direction),
|
||||
SwipableResult::Return(x) => Decision::Return(x),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,4 +230,4 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
impl<U> crate::ui::flow::Swipable for IconDialog<U> {}
|
||||
impl<U: Component> crate::ui::flow::Swipable<U::Msg> for IconDialog<U> {}
|
||||
|
@ -252,16 +252,17 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
impl<T> crate::ui::flow::Swipable for Frame<T>
|
||||
impl<T> crate::ui::flow::Swipable<FrameMsg<T::Msg>> for Frame<T>
|
||||
where
|
||||
T: Component + crate::ui::flow::Swipable,
|
||||
T: Component + crate::ui::flow::Swipable<T::Msg>,
|
||||
{
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: crate::ui::component::SwipeDirection,
|
||||
) -> bool {
|
||||
) -> crate::ui::flow::SwipableResult<FrameMsg<T::Msg>> {
|
||||
self.update_content(ctx, |ctx, inner| inner.swipe_start(ctx, direction))
|
||||
.map(|x| Some(FrameMsg::Content(x)))
|
||||
}
|
||||
|
||||
fn swipe_finished(&self) -> bool {
|
||||
|
@ -134,7 +134,7 @@ impl Component for PromptScreen {
|
||||
}
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
impl crate::ui::flow::Swipable for PromptScreen {}
|
||||
impl crate::ui::flow::Swipable<()> for PromptScreen {}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for PromptScreen {
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
ui::{
|
||||
animation::Animation,
|
||||
component::{Component, Event, EventCtx, Never, SwipeDirection},
|
||||
flow::Swipable,
|
||||
flow::{Swipable, SwipableResult},
|
||||
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||
model_mercury::component::Footer,
|
||||
shape,
|
||||
@ -160,8 +160,12 @@ impl<'a> Component for ShareWords<'a> {
|
||||
fn bounds(&self, _sink: &mut dyn FnMut(Rect)) {}
|
||||
}
|
||||
|
||||
impl<'a> Swipable for ShareWords<'a> {
|
||||
fn swipe_start(&mut self, ctx: &mut EventCtx, direction: SwipeDirection) -> bool {
|
||||
impl<'a> Swipable<Never> for ShareWords<'a> {
|
||||
fn swipe_start(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
direction: SwipeDirection,
|
||||
) -> SwipableResult<Never> {
|
||||
match direction {
|
||||
SwipeDirection::Up if !self.is_final_page() => {
|
||||
self.prev_index = self.page_index;
|
||||
@ -171,11 +175,11 @@ impl<'a> Swipable for ShareWords<'a> {
|
||||
self.prev_index = self.page_index;
|
||||
self.page_index = self.page_index.saturating_sub(1);
|
||||
}
|
||||
_ => return false,
|
||||
_ => return SwipableResult::Ignored,
|
||||
};
|
||||
if util::animation_disabled() {
|
||||
ctx.request_paint();
|
||||
return true;
|
||||
return SwipableResult::Animating;
|
||||
}
|
||||
self.animation = Some(Animation::new(
|
||||
0.0f32,
|
||||
@ -185,7 +189,7 @@ impl<'a> Swipable for ShareWords<'a> {
|
||||
));
|
||||
ctx.request_anim_frame();
|
||||
ctx.request_paint();
|
||||
true
|
||||
SwipableResult::Animating
|
||||
}
|
||||
|
||||
fn swipe_finished(&self) -> bool {
|
||||
|
@ -151,4 +151,4 @@ impl crate::trace::Trace for VerticalMenu {
|
||||
}
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
impl crate::ui::flow::Swipable for VerticalMenu {}
|
||||
impl crate::ui::flow::Swipable<VerticalMenuChoiceMsg> for VerticalMenu {}
|
||||
|
@ -89,7 +89,7 @@ impl ConfirmBlobParams {
|
||||
|
||||
pub fn into_layout(
|
||||
self,
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable + MaybeTrace, Error> {
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable<FlowMsg> + MaybeTrace, Error> {
|
||||
let paragraphs = ConfirmBlob {
|
||||
description: self.description.unwrap_or("".into()),
|
||||
extra: self.extra.unwrap_or("".into()),
|
||||
@ -187,7 +187,7 @@ impl ShowInfoParams {
|
||||
|
||||
pub fn into_layout(
|
||||
self,
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable + MaybeTrace, Error> {
|
||||
) -> Result<impl Component<Msg = FlowMsg> + Swipable<FlowMsg> + MaybeTrace, Error> {
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
let mut first: bool = true;
|
||||
for item in self.items {
|
||||
|
Loading…
Reference in New Issue
Block a user