mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
feat(core/ui): T3T1 confirm_action
Adds SwipeFlow for ConfirmAction or ConfirmActionSimple. Without animation as of now. [no changelog]
This commit is contained in:
parent
d8f20616be
commit
1ee36baea8
@ -358,6 +358,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_progress__x_seconds_left_template;
|
||||
MP_QSTR_progress_event;
|
||||
MP_QSTR_prompt;
|
||||
MP_QSTR_prompt_screen;
|
||||
MP_QSTR_qr_title;
|
||||
MP_QSTR_reboot_to_bootloader__just_a_moment;
|
||||
MP_QSTR_reboot_to_bootloader__restart;
|
||||
|
210
core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs
Normal file
210
core/embed/rust/src/ui/model_mercury/flow/confirm_action.rs
Normal file
@ -0,0 +1,210 @@
|
||||
use crate::{
|
||||
error,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{text::paragraphs::Paragraph, ComponentExt, SwipeDirection},
|
||||
flow::{base::Decision, flow_store, FlowMsg, FlowState, FlowStore, SwipeFlow, SwipePage},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, PromptScreen, VerticalMenu, VerticalMenuChoiceMsg},
|
||||
theme,
|
||||
};
|
||||
|
||||
// TODO: merge with code from https://github.com/trezor/trezor-firmware/pull/3805
|
||||
// when ready
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, ToPrimitive)]
|
||||
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> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
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::Confirm, FlowMsg::Confirmed) => Decision::Return(FlowMsg::Confirmed),
|
||||
(ConfirmAction::Confirm, FlowMsg::Info) => {
|
||||
Decision::Goto(ConfirmAction::Menu, SwipeDirection::Left)
|
||||
}
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlowState for ConfirmActionSimple {
|
||||
fn handle_swipe(&self, direction: SwipeDirection) -> Decision<Self> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event(&self, msg: FlowMsg) -> Decision<Self> {
|
||||
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),
|
||||
_ => Decision::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::{
|
||||
micropython::{map::Map, obj::Obj, qstr::Qstr, util},
|
||||
ui::{
|
||||
component::text::paragraphs::{ParagraphSource, ParagraphVecShort, VecExt},
|
||||
layout::obj::LayoutObj,
|
||||
},
|
||||
};
|
||||
|
||||
#[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) }
|
||||
}
|
||||
|
||||
fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
|
||||
let description: Option<TString> = kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
// let verb: Option<TString> = kwargs
|
||||
// .get(Qstr::MP_QSTR_verb)
|
||||
// .unwrap_or_else(|_| Obj::const_none())
|
||||
// .try_into_option()?;
|
||||
let verb_cancel: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
// let hold_danger: bool = kwargs.get_or(Qstr::MP_QSTR_hold_danger, false)?;
|
||||
let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, false)?;
|
||||
|
||||
let paragraphs = {
|
||||
let action = action.unwrap_or("".into());
|
||||
let description = description.unwrap_or("".into());
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
if !reverse {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, action))
|
||||
.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description));
|
||||
} else {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, description))
|
||||
.add(Paragraph::new(&theme::TEXT_MAIN_GREY_LIGHT, action));
|
||||
}
|
||||
paragraphs.into_paragraphs()
|
||||
};
|
||||
|
||||
let content_intro = Frame::left_aligned(title, SwipePage::vertical(paragraphs))
|
||||
.with_menu_button()
|
||||
.with_footer(TR::instructions__swipe_up.into(), None)
|
||||
.map(|msg| matches!(msg, FrameMsg::Button(_)).then_some(FlowMsg::Info));
|
||||
|
||||
let content_menu = if let Some(verb_cancel) = verb_cancel {
|
||||
Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty().danger(theme::ICON_CANCEL, verb_cancel.into()),
|
||||
)
|
||||
} else {
|
||||
Frame::left_aligned(
|
||||
"".into(),
|
||||
VerticalMenu::empty().danger(theme::ICON_CANCEL, TR::buttons__cancel.into()),
|
||||
)
|
||||
}
|
||||
.with_cancel_button()
|
||||
.map(move |msg| match msg {
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(_)) => Some(FlowMsg::Choice(0)),
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Cancelled),
|
||||
});
|
||||
|
||||
if !prompt_screen {
|
||||
let store = flow_store().add(content_intro)?.add(content_menu)?;
|
||||
let res = SwipeFlow::new(ConfirmActionSimple::Intro, store)?;
|
||||
return Ok(LayoutObj::new(res)?.into());
|
||||
} else {
|
||||
let (prompt, prompt_action) = if hold {
|
||||
(
|
||||
PromptScreen::new_hold_to_confirm(),
|
||||
TR::instructions__hold_to_confirm.into(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
PromptScreen::new_tap_to_confirm(),
|
||||
TR::instructions__tap_to_confirm.into(),
|
||||
)
|
||||
};
|
||||
|
||||
let content_confirm = Frame::left_aligned(title, prompt)
|
||||
.with_footer(prompt_action, None)
|
||||
.with_menu_button();
|
||||
// .with_overlapping_content();
|
||||
|
||||
// if let Some(subtitle) = subtitle {
|
||||
// content_confirm = content_confirm.with_subtitle(subtitle);
|
||||
// }
|
||||
|
||||
let content_confirm = content_confirm.map(move |msg| match msg {
|
||||
FrameMsg::Content(()) => Some(FlowMsg::Confirmed),
|
||||
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)?;
|
||||
return Ok(LayoutObj::new(res)?.into());
|
||||
};
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod confirm_action;
|
||||
pub mod confirm_reset_create;
|
||||
pub mod confirm_reset_recover;
|
||||
pub mod confirm_set_new_pin;
|
||||
@ -6,6 +7,7 @@ pub mod prompt_backup;
|
||||
pub mod show_share_words;
|
||||
pub mod warning_hi_prio;
|
||||
|
||||
pub use confirm_action::new_confirm_action;
|
||||
pub use confirm_reset_create::ConfirmResetCreate;
|
||||
pub use confirm_reset_recover::ConfirmResetRecover;
|
||||
pub use confirm_set_new_pin::SetNewPin;
|
||||
|
@ -331,54 +331,6 @@ impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let action: Option<TString> = kwargs.get(Qstr::MP_QSTR_action)?.try_into_option()?;
|
||||
let description: Option<TString> =
|
||||
kwargs.get(Qstr::MP_QSTR_description)?.try_into_option()?;
|
||||
let verb: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let verb_cancel: Option<TString> = kwargs
|
||||
.get(Qstr::MP_QSTR_verb_cancel)
|
||||
.unwrap_or_else(|_| Obj::const_none())
|
||||
.try_into_option()?;
|
||||
let reverse: bool = kwargs.get_or(Qstr::MP_QSTR_reverse, false)?;
|
||||
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
|
||||
let hold_danger: bool = kwargs.get_or(Qstr::MP_QSTR_hold_danger, false)?;
|
||||
|
||||
let paragraphs = {
|
||||
let action = action.unwrap_or("".into());
|
||||
let description = description.unwrap_or("".into());
|
||||
let mut paragraphs = ParagraphVecShort::new();
|
||||
if !reverse {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action))
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description));
|
||||
} else {
|
||||
paragraphs
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description))
|
||||
.add(Paragraph::new(&theme::TEXT_DEMIBOLD, action));
|
||||
}
|
||||
paragraphs.into_paragraphs()
|
||||
};
|
||||
|
||||
let mut page = if hold {
|
||||
ButtonPage::new(paragraphs, theme::BG).with_hold()?
|
||||
} else {
|
||||
ButtonPage::new(paragraphs, theme::BG).with_cancel_confirm(verb_cancel, verb)
|
||||
};
|
||||
if hold && hold_danger {
|
||||
page = page.with_confirm_style(theme::button_danger())
|
||||
}
|
||||
let obj = LayoutObj::new(Frame::left_aligned(title, page))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
@ -1610,14 +1562,16 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// title: str,
|
||||
/// action: str | None,
|
||||
/// description: str | None,
|
||||
/// subtitle: str | None = None,
|
||||
/// verb: str | None = None,
|
||||
/// verb_cancel: str | None = None,
|
||||
/// hold: bool = False,
|
||||
/// hold_danger: bool = False,
|
||||
/// reverse: bool = False,
|
||||
/// prompt_screen: bool = False,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Confirm action."""
|
||||
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, new_confirm_action).as_obj(),
|
||||
Qstr::MP_QSTR_confirm_action => obj_fn_kw!(0, flow::confirm_action::new_confirm_action).as_obj(),
|
||||
|
||||
/// def confirm_emphasized(
|
||||
/// *,
|
||||
|
@ -76,11 +76,13 @@ def confirm_action(
|
||||
title: str,
|
||||
action: str | None,
|
||||
description: str | None,
|
||||
subtitle: str | None = None,
|
||||
verb: str | None = None,
|
||||
verb_cancel: str | None = None,
|
||||
hold: bool = False,
|
||||
hold_danger: bool = False,
|
||||
reverse: bool = False,
|
||||
prompt_screen: bool = False,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Confirm action."""
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user