mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
refactor(core): move tutorial to UiFeatures
- unsupported on model_t
This commit is contained in:
parent
d4fa752709
commit
36f4c46389
@ -358,6 +358,14 @@ extern "C" fn new_show_wait_text(message: Obj) -> Obj {
|
||||
unsafe { util::try_or_raise(block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], _kwargs: &Map| {
|
||||
let layout = ModelUI::tutorial()?;
|
||||
Ok(LayoutObj::new_root(layout)?.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
pub extern "C" fn upy_check_homescreen_format(data: Obj) -> Obj {
|
||||
let block = || {
|
||||
let buffer = data.try_into()?;
|
||||
@ -654,6 +662,10 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
||||
/// """Show single-line text in the middle of the screen."""
|
||||
Qstr::MP_QSTR_show_wait_text => obj_fn_1!(new_show_wait_text).as_obj(),
|
||||
|
||||
/// def tutorial() -> LayoutObj[UiResult]:
|
||||
/// """Show user how to interact with the device."""
|
||||
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, new_tutorial).as_obj(),
|
||||
|
||||
/// class BacklightLevels:
|
||||
/// """Backlight levels. Values dynamically update based on user settings."""
|
||||
/// MAX: ClassVar[int]
|
||||
|
@ -1015,15 +1015,6 @@ extern "C" fn new_prompt_backup() -> Obj {
|
||||
unsafe { util::try_or_raise(block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_show_tutorial() -> Obj {
|
||||
let block = || {
|
||||
let flow = flow::show_tutorial::new_show_tutorial()?;
|
||||
let obj = LayoutObj::new_root(flow)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_or_raise(block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_show_group_share_success(
|
||||
n_args: usize,
|
||||
args: *const Obj,
|
||||
@ -1323,10 +1314,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Shown after successfully finishing a group."""
|
||||
Qstr::MP_QSTR_show_group_share_success => obj_fn_kw!(0, new_show_group_share_success).as_obj(),
|
||||
|
||||
/// def tutorial() -> LayoutObj[UiResult]:
|
||||
/// """Show user how to interact with the device."""
|
||||
Qstr::MP_QSTR_tutorial => obj_fn_0!(new_show_tutorial).as_obj(),
|
||||
|
||||
/// def flow_get_address(
|
||||
/// *,
|
||||
/// address: str | bytes,
|
||||
|
@ -356,4 +356,9 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
|
||||
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
|
||||
let flow = flow::show_tutorial::new_show_tutorial()?;
|
||||
Ok(flow)
|
||||
}
|
||||
}
|
||||
|
@ -753,95 +753,6 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
/// General pattern of most tutorial screens.
|
||||
/// (title, text, btn_layout, btn_actions, text_y_offset)
|
||||
fn tutorial_screen(
|
||||
title: TString<'static>,
|
||||
text: TR,
|
||||
btn_layout: ButtonLayout,
|
||||
btn_actions: ButtonActions,
|
||||
) -> Page {
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
||||
}
|
||||
|
||||
extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], _kwargs: &Map| {
|
||||
const PAGE_COUNT: usize = 7;
|
||||
|
||||
let get_page = move |page_index| {
|
||||
// Lazy-loaded list of screens to show, with custom content,
|
||||
// buttons and actions triggered by these buttons.
|
||||
// Cancelling the first screen will point to the last one,
|
||||
// which asks for confirmation whether user wants to
|
||||
// really cancel the tutorial.
|
||||
match page_index {
|
||||
// title, text, btn_layout, btn_actions
|
||||
0 => tutorial_screen(
|
||||
TR::tutorial__title_hello.into(),
|
||||
TR::tutorial__welcome_press_right,
|
||||
ButtonLayout::cancel_none_arrow(),
|
||||
ButtonActions::last_none_next(),
|
||||
),
|
||||
1 => tutorial_screen(
|
||||
"".into(),
|
||||
TR::tutorial__use_trezor,
|
||||
ButtonLayout::arrow_none_arrow(),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
2 => tutorial_screen(
|
||||
TR::buttons__hold_to_confirm.into(),
|
||||
TR::tutorial__press_and_hold,
|
||||
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
3 => tutorial_screen(
|
||||
TR::tutorial__title_screen_scroll.into(),
|
||||
TR::tutorial__scroll_down,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
4 => tutorial_screen(
|
||||
TR::buttons__confirm.into(),
|
||||
TR::tutorial__middle_click,
|
||||
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
|
||||
ButtonActions::none_next_none(),
|
||||
),
|
||||
5 => tutorial_screen(
|
||||
TR::tutorial__title_tutorial_complete.into(),
|
||||
TR::tutorial__ready_to_use,
|
||||
ButtonLayout::text_none_text(
|
||||
TR::buttons__again.into(),
|
||||
TR::buttons__continue.into(),
|
||||
),
|
||||
ButtonActions::beginning_none_confirm(),
|
||||
),
|
||||
6 => tutorial_screen(
|
||||
TR::tutorial__title_skip.into(),
|
||||
TR::tutorial__sure_you_want_skip,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
|
||||
ButtonActions::beginning_none_cancel(),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
let pages = FlowPages::new(get_page, PAGE_COUNT);
|
||||
|
||||
// Setting the ignore-second-button to mimic all the Choice pages, to teach user
|
||||
// that they should really press both buttons at the same time to achieve
|
||||
// middle-click.
|
||||
let obj = LayoutObj::new(
|
||||
Flow::new(pages)
|
||||
.with_scrollbar(false)
|
||||
.with_ignore_second_button_ms(constant::IGNORE_OTHER_BTN_MS),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
}
|
||||
|
||||
extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?;
|
||||
@ -1364,10 +1275,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// """Confirm details about altcoin transaction."""
|
||||
Qstr::MP_QSTR_altcoin_tx_summary => obj_fn_kw!(0, new_altcoin_tx_summary).as_obj(),
|
||||
|
||||
/// def tutorial() -> LayoutObj[UiResult]:
|
||||
/// """Show user how to interact with the device."""
|
||||
Qstr::MP_QSTR_tutorial => obj_fn_kw!(0, tutorial).as_obj(),
|
||||
|
||||
/// def confirm_modify_fee(
|
||||
/// *,
|
||||
/// title: str, # ignored
|
||||
|
@ -23,7 +23,10 @@ use crate::{
|
||||
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||
util::RecoveryType,
|
||||
},
|
||||
model_tr::component::{ButtonActions, ButtonLayout, Page},
|
||||
model_tr::{
|
||||
component::{ButtonActions, ButtonLayout, Page},
|
||||
constant,
|
||||
},
|
||||
ui_features_fw::UIFeaturesFirmware,
|
||||
},
|
||||
};
|
||||
@ -389,6 +392,79 @@ impl UIFeaturesFirmware for ModelTRFeatures {
|
||||
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
|
||||
const PAGE_COUNT: usize = 7;
|
||||
|
||||
let get_page = move |page_index| {
|
||||
// Lazy-loaded list of screens to show, with custom content,
|
||||
// buttons and actions triggered by these buttons.
|
||||
// Cancelling the first screen will point to the last one,
|
||||
// which asks for confirmation whether user wants to
|
||||
// really cancel the tutorial.
|
||||
match page_index {
|
||||
// title, text, btn_layout, btn_actions
|
||||
0 => tutorial_screen(
|
||||
TR::tutorial__title_hello.into(),
|
||||
TR::tutorial__welcome_press_right,
|
||||
ButtonLayout::cancel_none_arrow(),
|
||||
ButtonActions::last_none_next(),
|
||||
),
|
||||
1 => tutorial_screen(
|
||||
"".into(),
|
||||
TR::tutorial__use_trezor,
|
||||
ButtonLayout::arrow_none_arrow(),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
2 => tutorial_screen(
|
||||
TR::buttons__hold_to_confirm.into(),
|
||||
TR::tutorial__press_and_hold,
|
||||
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
3 => tutorial_screen(
|
||||
TR::tutorial__title_screen_scroll.into(),
|
||||
TR::tutorial__scroll_down,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
4 => tutorial_screen(
|
||||
TR::buttons__confirm.into(),
|
||||
TR::tutorial__middle_click,
|
||||
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
|
||||
ButtonActions::none_next_none(),
|
||||
),
|
||||
5 => tutorial_screen(
|
||||
TR::tutorial__title_tutorial_complete.into(),
|
||||
TR::tutorial__ready_to_use,
|
||||
ButtonLayout::text_none_text(
|
||||
TR::buttons__again.into(),
|
||||
TR::buttons__continue.into(),
|
||||
),
|
||||
ButtonActions::beginning_none_confirm(),
|
||||
),
|
||||
6 => tutorial_screen(
|
||||
TR::tutorial__title_skip.into(),
|
||||
TR::tutorial__sure_you_want_skip,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
|
||||
ButtonActions::beginning_none_cancel(),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
};
|
||||
|
||||
let pages = FlowPages::new(get_page, PAGE_COUNT);
|
||||
|
||||
// Setting the ignore-second-button to mimic all the Choice pages, to teach user
|
||||
// that they should really press both buttons at the same time to achieve
|
||||
// middle-click.
|
||||
let layout = RootComponent::new(
|
||||
Flow::new(pages)
|
||||
.with_scrollbar(false)
|
||||
.with_ignore_second_button_ms(constant::IGNORE_OTHER_BTN_MS),
|
||||
);
|
||||
Ok(layout)
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to create and call a `ButtonPage` dialog based on paginable content
|
||||
@ -427,3 +503,16 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
|
||||
Ok(RootComponent::new(frame))
|
||||
}
|
||||
|
||||
/// General pattern of most tutorial screens.
|
||||
/// (title, text, btn_layout, btn_actions, text_y_offset)
|
||||
fn tutorial_screen(
|
||||
title: TString<'static>,
|
||||
text: TR,
|
||||
btn_layout: ButtonLayout,
|
||||
btn_actions: ButtonActions,
|
||||
) -> Page {
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
||||
}
|
||||
|
@ -440,6 +440,12 @@ impl UIFeaturesFirmware for ModelTTFeatures {
|
||||
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
|
||||
Err::<RootComponent<Empty, ModelTTFeatures>, Error>(Error::ValueError(
|
||||
c"tutorial not supported",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn new_show_modal(
|
||||
|
@ -120,4 +120,6 @@ pub trait UIFeaturesFirmware {
|
||||
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
|
||||
|
||||
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error>;
|
||||
|
||||
fn tutorial() -> Result<impl LayoutMaybeTrace, Error>;
|
||||
}
|
||||
|
@ -262,11 +262,6 @@ def show_group_share_success(
|
||||
"""Shown after successfully finishing a group."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def tutorial() -> LayoutObj[UiResult]:
|
||||
"""Show user how to interact with the device."""
|
||||
|
||||
|
||||
# rust/src/ui/model_mercury/layout.rs
|
||||
def flow_get_address(
|
||||
*,
|
||||
@ -481,11 +476,6 @@ def altcoin_tx_summary(
|
||||
"""Confirm details about altcoin transaction."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def tutorial() -> LayoutObj[UiResult]:
|
||||
"""Show user how to interact with the device."""
|
||||
|
||||
|
||||
# rust/src/ui/model_tr/layout.rs
|
||||
def confirm_modify_fee(
|
||||
*,
|
||||
|
@ -281,6 +281,11 @@ def show_wait_text(message: str, /) -> LayoutObj[None]:
|
||||
"""Show single-line text in the middle of the screen."""
|
||||
|
||||
|
||||
# rust/src/ui/api/firmware_upy.rs
|
||||
def tutorial() -> LayoutObj[UiResult]:
|
||||
"""Show user how to interact with the device."""
|
||||
|
||||
|
||||
# rust/src/ui/api/firmware_upy.rs
|
||||
class BacklightLevels:
|
||||
"""Backlight levels. Values dynamically update based on user settings."""
|
||||
|
@ -1204,7 +1204,7 @@ def set_brightness(current: int | None = None) -> Awaitable[None]:
|
||||
def tutorial(br_code: ButtonRequestType = BR_CODE_OTHER) -> Awaitable[None]:
|
||||
"""Showing users how to interact with the device."""
|
||||
return raise_if_not_confirmed(
|
||||
trezorui2.tutorial(),
|
||||
trezorui_api.tutorial(),
|
||||
"tutorial",
|
||||
br_code,
|
||||
)
|
||||
|
@ -487,7 +487,7 @@ async def confirm_output(
|
||||
|
||||
def tutorial(br_code: ButtonRequestType = BR_CODE_OTHER) -> Awaitable[ui.UiResult]:
|
||||
"""Showing users how to interact with the device."""
|
||||
return interact(trezorui2.tutorial(), "tutorial", br_code)
|
||||
return interact(trezorui_api.tutorial(), "tutorial", br_code)
|
||||
|
||||
|
||||
async def should_show_payment_request_details(
|
||||
|
Loading…
Reference in New Issue
Block a user