mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 09:28:13 +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) }
|
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 {
|
pub extern "C" fn upy_check_homescreen_format(data: Obj) -> Obj {
|
||||||
let block = || {
|
let block = || {
|
||||||
let buffer = data.try_into()?;
|
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."""
|
/// """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(),
|
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:
|
/// class BacklightLevels:
|
||||||
/// """Backlight levels. Values dynamically update based on user settings."""
|
/// """Backlight levels. Values dynamically update based on user settings."""
|
||||||
/// MAX: ClassVar[int]
|
/// MAX: ClassVar[int]
|
||||||
|
@ -1015,15 +1015,6 @@ extern "C" fn new_prompt_backup() -> Obj {
|
|||||||
unsafe { util::try_or_raise(block) }
|
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(
|
extern "C" fn new_show_group_share_success(
|
||||||
n_args: usize,
|
n_args: usize,
|
||||||
args: *const Obj,
|
args: *const Obj,
|
||||||
@ -1323,10 +1314,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
|||||||
/// """Shown after successfully finishing a group."""
|
/// """Shown after successfully finishing a group."""
|
||||||
Qstr::MP_QSTR_show_group_share_success => obj_fn_kw!(0, new_show_group_share_success).as_obj(),
|
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(
|
/// def flow_get_address(
|
||||||
/// *,
|
/// *,
|
||||||
/// address: str | bytes,
|
/// address: str | bytes,
|
||||||
|
@ -356,4 +356,9 @@ impl UIFeaturesFirmware for ModelMercuryFeatures {
|
|||||||
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||||
Ok(layout)
|
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) }
|
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 {
|
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 block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let sign: i32 = kwargs.get(Qstr::MP_QSTR_sign)?.try_into()?;
|
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."""
|
/// """Confirm details about altcoin transaction."""
|
||||||
Qstr::MP_QSTR_altcoin_tx_summary => obj_fn_kw!(0, new_altcoin_tx_summary).as_obj(),
|
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(
|
/// def confirm_modify_fee(
|
||||||
/// *,
|
/// *,
|
||||||
/// title: str, # ignored
|
/// title: str, # ignored
|
||||||
|
@ -23,7 +23,10 @@ use crate::{
|
|||||||
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
obj::{LayoutMaybeTrace, LayoutObj, RootComponent},
|
||||||
util::RecoveryType,
|
util::RecoveryType,
|
||||||
},
|
},
|
||||||
model_tr::component::{ButtonActions, ButtonLayout, Page},
|
model_tr::{
|
||||||
|
component::{ButtonActions, ButtonLayout, Page},
|
||||||
|
constant,
|
||||||
|
},
|
||||||
ui_features_fw::UIFeaturesFirmware,
|
ui_features_fw::UIFeaturesFirmware,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -389,6 +392,79 @@ impl UIFeaturesFirmware for ModelTRFeatures {
|
|||||||
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||||
Ok(layout)
|
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
|
/// 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))
|
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));
|
let layout = RootComponent::new(Connect::new(text, theme::FG, theme::BG));
|
||||||
Ok(layout)
|
Ok(layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tutorial() -> Result<impl LayoutMaybeTrace, Error> {
|
||||||
|
Err::<RootComponent<Empty, ModelTTFeatures>, Error>(Error::ValueError(
|
||||||
|
c"tutorial not supported",
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_show_modal(
|
fn new_show_modal(
|
||||||
|
@ -120,4 +120,6 @@ pub trait UIFeaturesFirmware {
|
|||||||
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
|
) -> Result<Gc<LayoutObj>, Error>; // TODO: return LayoutMaybeTrace
|
||||||
|
|
||||||
fn show_wait_text(text: TString<'static>) -> Result<impl LayoutMaybeTrace, Error>;
|
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."""
|
"""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
|
# rust/src/ui/model_mercury/layout.rs
|
||||||
def flow_get_address(
|
def flow_get_address(
|
||||||
*,
|
*,
|
||||||
@ -481,11 +476,6 @@ def altcoin_tx_summary(
|
|||||||
"""Confirm details about altcoin transaction."""
|
"""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
|
# rust/src/ui/model_tr/layout.rs
|
||||||
def confirm_modify_fee(
|
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."""
|
"""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
|
# rust/src/ui/api/firmware_upy.rs
|
||||||
class BacklightLevels:
|
class BacklightLevels:
|
||||||
"""Backlight levels. Values dynamically update based on user settings."""
|
"""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]:
|
def tutorial(br_code: ButtonRequestType = BR_CODE_OTHER) -> Awaitable[None]:
|
||||||
"""Showing users how to interact with the device."""
|
"""Showing users how to interact with the device."""
|
||||||
return raise_if_not_confirmed(
|
return raise_if_not_confirmed(
|
||||||
trezorui2.tutorial(),
|
trezorui_api.tutorial(),
|
||||||
"tutorial",
|
"tutorial",
|
||||||
br_code,
|
br_code,
|
||||||
)
|
)
|
||||||
|
@ -487,7 +487,7 @@ async def confirm_output(
|
|||||||
|
|
||||||
def tutorial(br_code: ButtonRequestType = BR_CODE_OTHER) -> Awaitable[ui.UiResult]:
|
def tutorial(br_code: ButtonRequestType = BR_CODE_OTHER) -> Awaitable[ui.UiResult]:
|
||||||
"""Showing users how to interact with the device."""
|
"""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(
|
async def should_show_payment_request_details(
|
||||||
|
Loading…
Reference in New Issue
Block a user