1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-01 11:28:20 +00:00

WIP - fixes, wording, tutorial

This commit is contained in:
grdddj 2022-11-04 11:19:43 +01:00
parent 51a65b9c26
commit 53f5122c73
11 changed files with 151 additions and 96 deletions

View File

@ -8,12 +8,10 @@ use heapless::LinearMap;
use crate::ui::{ use crate::ui::{
component::{Component, Event, EventCtx, Never}, component::{Component, Event, EventCtx, Never},
display::{Color, Font}, display::{Color, Font},
geometry::{Rect}, geometry::Rect,
}; };
use super::layout::{ use super::layout::{LayoutFit, LayoutSink, LineBreaking, Op, TextLayout, TextRenderer, TextStyle};
LayoutFit, LayoutSink, LineBreaking, Op, TextLayout, TextRenderer, TextStyle,
};
pub const MAX_ARGUMENTS: usize = 6; pub const MAX_ARGUMENTS: usize = 6;

View File

@ -1,8 +1,13 @@
use crate::{ui::{constant, geometry::{Point, Offset, Rect}}, trezorhal::display}; use crate::{
trezorhal::display,
ui::{
constant,
geometry::{Offset, Point, Rect},
},
};
use core::slice; use core::slice;
use super::{Color, get_color_table, pixeldata, set_window, get_offset}; use super::{get_color_table, get_offset, pixeldata, set_window, Color};
pub struct Glyph { pub struct Glyph {
pub width: i16, pub width: i16,

View File

@ -576,6 +576,15 @@ impl ButtonLayout<&'static str> {
) )
} }
/// Left and right texts.
pub fn left_right_text(text_left: &'static str, text_right: &'static str) -> Self {
Self::new(
Some(ButtonDetails::text(text_left)),
None,
Some(ButtonDetails::text(text_right)),
)
}
/// Left and right arrow icons for navigation. /// Left and right arrow icons for navigation.
pub fn left_right_arrows() -> Self { pub fn left_right_arrows() -> Self {
Self::new( Self::new(
@ -611,6 +620,29 @@ impl ButtonLayout<&'static str> {
Some(ButtonDetails::text(text).with_duration(duration)), Some(ButtonDetails::text(text).with_duration(duration)),
) )
} }
/// Arrow back on left and hold-to-confirm text on the right.
pub fn back_and_htc_text(text: &'static str, duration: Duration) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text).with_duration(duration)),
)
}
/// Arrow back on left and text on the right.
pub fn back_and_text(text: &'static str) -> Self {
Self::new(
Some(ButtonDetails::left_arrow_icon()),
None,
Some(ButtonDetails::text(text)),
)
}
/// Only armed text in the middle.
pub fn middle_armed_text(text: &'static str) -> Self {
Self::new(None, Some(ButtonDetails::armed_text(text)), None)
}
} }
/// What happens when a button is triggered. /// What happens when a button is triggered.

View File

@ -1,7 +1,8 @@
use super::button::{Button, ButtonMsg::Clicked}; use super::button::{Button, ButtonMsg::Clicked};
use crate::ui::{ use crate::ui::{
component::{Child, Component, Event, EventCtx}, component::{Child, Component, Event, EventCtx},
geometry::Rect, model_tr::theme, geometry::Rect,
model_tr::theme,
}; };
pub enum DialogMsg<T> { pub enum DialogMsg<T> {

View File

@ -167,7 +167,8 @@ impl<T: AsRef<str>> Loader<T> {
// TODO: support painting icons // TODO: support painting icons
if let Some(text_overlay) = &mut self.text_overlay { if let Some(text_overlay) = &mut self.text_overlay {
// NOTE: need to calculate this in `i32`, it would overflow using `i16` // NOTE: need to calculate this in `i32`, it would overflow using `i16`
let invert_from = ((self.area.width() as i32 + 1) * done) / (display::LOADER_MAX as i32); let invert_from =
((self.area.width() as i32 + 1) * done) / (display::LOADER_MAX as i32);
// TODO: the text should be moved one pixel to the top so it is centered in the // TODO: the text should be moved one pixel to the top so it is centered in the
// loader // loader

View File

@ -36,6 +36,7 @@ pub use choice_item::ChoiceItem;
pub use dialog::{Dialog, DialogMsg}; pub use dialog::{Dialog, DialogMsg};
pub use flow::{Flow, FlowMsg}; pub use flow::{Flow, FlowMsg};
pub use flow_pages::{FlowPages, Page}; pub use flow_pages::{FlowPages, Page};
pub use flow_pages_poc_helpers::LineAlignment;
pub use frame::Frame; pub use frame::Frame;
pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet}; pub use loader::{Loader, LoaderMsg, LoaderStyle, LoaderStyleSheet};
pub use page::ButtonPage; pub use page::ButtonPage;

View File

@ -28,6 +28,7 @@ use crate::{
util::iter_into_vec, util::iter_into_vec,
util::upy_disable_animation, util::upy_disable_animation,
}, },
model_tr::component::LineAlignment,
}, },
}; };
@ -305,6 +306,34 @@ extern "C" fn confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Map) -
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)
fn tutorial_screen(
data: (
StrBuffer,
StrBuffer,
ButtonLayout<&'static str>,
ButtonActions,
),
) -> Page<10> {
let (title, text, btn_layout, btn_actions) = data;
let mut page = Page::<10>::new(
btn_layout,
btn_actions,
if !title.is_empty() {
Font::BOLD
} else {
Font::MONO
},
);
// Add title if present
if !title.is_empty() {
page = page.text_bold(title).newline().newline_half()
}
page = page.text_mono(text);
page
}
extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj { extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
let block = |_args: &[Obj], _kwargs: &Map| { let block = |_args: &[Obj], _kwargs: &Map| {
const PAGE_COUNT: u8 = 7; const PAGE_COUNT: u8 = 7;
@ -315,83 +344,72 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
// Cancelling the first screen will point to the last one, // Cancelling the first screen will point to the last one,
// which asks for confirmation whether user wants to // which asks for confirmation whether user wants to
// really cancel the tutorial. // really cancel the tutorial.
let screen = match page_index { match page_index {
// title, text, btn_layout, btn_actions // title, text, btn_layout, btn_actions
0 => ( 0 => {
"HELLO", tutorial_screen((
"Welcome to Trezor.\nPress right to continue.", "HELLO".into(),
"Welcome to Trezor.\nPress right to continue.".into(),
ButtonLayout::cancel_and_arrow(), ButtonLayout::cancel_and_arrow(),
ButtonActions::last_next(), ButtonActions::last_next(),
), ))
1 => ( },
"", 1 => {
"Use Trezor by clicking left & right.\n\nContinue right.", tutorial_screen((
"".into(),
"Use Trezor by clicking left and right.\n\nContinue right.".into(),
ButtonLayout::left_right_arrows(), ButtonLayout::left_right_arrows(),
ButtonActions::prev_next(), ButtonActions::prev_next(),
), ))
2 => ( },
"HOLD TO CONFIRM", 2 => {
"Press & hold right to approve important operations.", tutorial_screen((
ButtonLayout::new( "HOLD TO CONFIRM".into(),
Some(ButtonDetails::left_arrow_icon()), "Press and hold right to approve important operations.".into(),
None, ButtonLayout::back_and_htc_text("HOLD TO CONFIRM", Duration::from_millis(1000)),
Some(
ButtonDetails::text("HOLD TO CONFIRM")
.with_duration(Duration::from_millis(2000)),
),
),
ButtonActions::prev_next(), ButtonActions::prev_next(),
), ))
3 => ( },
"SCREEN SCROLL", 3 => {
"Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.", tutorial_screen((
ButtonLayout::new( "SCREEN SCROLL".into(),
Some(ButtonDetails::left_arrow_icon()), "Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.".into(),
None, ButtonLayout::back_and_text("GOT IT"),
Some(ButtonDetails::text("GOT IT")), ),
ButtonActions::prev_next(), ButtonActions::prev_next(),
), ))
4 => ( },
"CONFIRM", 4 => {
"Press both left & right at the same time to confirm.", tutorial_screen((
ButtonLayout::new( "CONFIRM".into(),
Some(ButtonDetails::left_arrow_icon()), "Press both left and right at the same time to confirm.".into(),
Some(ButtonDetails::armed_text("CONFIRM")), ButtonLayout::middle_armed_text("CONFIRM"),
None,
),
ButtonActions::prev_next_with_middle(), ButtonActions::prev_next_with_middle(),
), ))
5 => ( },
"CONGRATS!", // This page is special
"You're ready to use Trezor.", 5 => {
ButtonLayout::new( Page::<10>::new(
Some(ButtonDetails::text("AGAIN")), ButtonLayout::left_right_text("AGAIN", "FINISH"),
None,
Some(ButtonDetails::text("FINISH")),
),
ButtonActions::beginning_confirm(), ButtonActions::beginning_confirm(),
), Font::MONO,
6 => ( )
"SKIP TUTORIAL", .newline()
"Sure you want to skip the tutorial?", .text_mono("Tutorial complete.".into())
ButtonLayout::new( .newline()
Some(ButtonDetails::left_arrow_icon()), .newline()
None, .alignment(LineAlignment::Center)
Some(ButtonDetails::text("SKIP")), .text_bold("You're ready to\nuse Trezor.".into())
), },
6 => {
tutorial_screen((
"SKIP TUTORIAL".into(),
"Are you sure you want to skip the tutorial?".into(),
ButtonLayout::cancel_and_text("SKIP"),
ButtonActions::beginning_cancel(), ButtonActions::beginning_cancel(),
), ))
},
_ => unreachable!(), _ => unreachable!(),
};
let mut page = Page::<10>::new(screen.2.clone(), screen.3.clone(), Font::BOLD);
// Add title if present
if !screen.0.is_empty() {
page = page.text_bold(screen.0.into()).newline().newline_half()
} }
page = page.text_mono(screen.1.into());
page
}; };
let pages = FlowPages::new(get_page, PAGE_COUNT); let pages = FlowPages::new(get_page, PAGE_COUNT);
@ -532,6 +550,7 @@ extern "C" fn select_word(n_args: usize, args: *const Obj, kwargs: *mut Map) ->
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?; let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
let words: Vec<StrBuffer, 3> = iter_into_vec(words_iterable)?; let words: Vec<StrBuffer, 3> = iter_into_vec(words_iterable)?;
// TODO: should return int, to be consistent with TT's select_word
let obj = LayoutObj::new(Frame::new(title, SimpleChoice::new(words).into_child()))?; let obj = LayoutObj::new(Frame::new(title, SimpleChoice::new(words).into_child()))?;
Ok(obj.into()) Ok(obj.into())
}; };
@ -660,9 +679,8 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// *, /// *,
/// title: str, /// title: str,
/// words: Iterable[str], /// words: Iterable[str],
/// ) -> int: /// ) -> str:
/// """Select mnemonic word from three possibilities - seed check after backup. The /// """Select a word from a list. TODO: should return int, to be consistent with TT's select_word"""
/// iterable must be of exact size. Returns index in range `0..3`."""
Qstr::MP_QSTR_select_word => obj_fn_kw!(0, select_word).as_obj(), Qstr::MP_QSTR_select_word => obj_fn_kw!(0, select_word).as_obj(),
/// def request_word_count( /// def request_word_count(

View File

@ -121,9 +121,8 @@ def select_word(
*, *,
title: str, title: str,
words: Iterable[str], words: Iterable[str],
) -> int: ) -> str:
"""Select mnemonic word from three possibilities - seed check after backup. The """Select a word from a list. TODO: should return int, to be consistent with TT's select_word"""
iterable must be of exact size. Returns index in range `0..3`."""
# rust/src/ui/model_tr/layout.rs # rust/src/ui/model_tr/layout.rs

View File

@ -561,8 +561,8 @@ async def confirm_reset_device(
return await _placeholder_confirm( return await _placeholder_confirm(
ctx=ctx, ctx=ctx,
br_type="recover_device" if recovery else "setup_device", br_type="recover_device" if recovery else "setup_device",
title="RECOVERY MODE" if recovery else "CREATE NEW WALLET", title="START RECOVERY" if recovery else "CREATE NEW WALLET",
data="By continuing you agree to trezor.io/tos", data="By continuing you agree to our terms and conditions.\nSee trezor.io/tos.",
description=prompt, description=prompt,
br_code=ButtonRequestType.ProtectCall br_code=ButtonRequestType.ProtectCall
if recovery if recovery

View File

@ -51,10 +51,10 @@ async def select_word(
) )
) )
) )
if __debug__ and isinstance(result, str): for word in words:
return result if word.upper() == result:
assert isinstance(result, int) and 0 <= result <= 2 return word
return words[result] raise ValueError("Invalid word")
async def slip39_show_checklist( async def slip39_show_checklist(