1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-17 21:22:10 +00:00

chore(core/rust): improve the design of TR's tutorial flow

[no changelog]
This commit is contained in:
grdddj 2023-06-16 11:37:17 +02:00 committed by Jiří Musil
parent 947e2ee24f
commit 13cb1ea4ce
4 changed files with 45 additions and 41 deletions

View File

@ -101,6 +101,11 @@ impl<'a, T: StringType + Clone + 'a> OpTextLayout<T> {
// Try to fit text on the current page and if they do not fit,
// return the appropriate OutOfBounds message
// Inserting the ellipsis at the very beginning of the text if needed
// (just once for the first Op::Text on the non-first page).
self.layout.continues_from_prev_page =
skip_bytes > 0 && total_processed_chars == 0;
let fit = self.layout.layout_text(text.as_ref(), cursor, sink);
match fit {

View File

@ -29,6 +29,7 @@ where
buttons: Child<ButtonController<T>>,
page_counter: usize,
return_confirmed_index: bool,
show_scrollbar: bool,
}
impl<F, T> Flow<F, T>
@ -52,6 +53,7 @@ where
buttons: Child::new(ButtonController::new(ButtonLayout::empty())),
page_counter: 0,
return_confirmed_index: false,
show_scrollbar: true,
}
}
@ -68,6 +70,12 @@ where
self
}
/// Show scrollbar or not.
pub fn with_scrollbar(mut self, show_scrollbar: bool) -> Self {
self.show_scrollbar = show_scrollbar;
self
}
pub fn confirmed_index(&self) -> Option<usize> {
self.return_confirmed_index.then_some(self.page_counter)
}
@ -203,8 +211,11 @@ where
// (scrollbar will be active - counting pages - even when not placed and
// painted)
if self.title.is_some() {
let (title_area, scrollbar_area) =
title_area.split_right(self.scrollbar.inner().overall_width() + SCROLLBAR_SPACE);
let (title_area, scrollbar_area) = if self.show_scrollbar {
title_area.split_right(self.scrollbar.inner().overall_width() + SCROLLBAR_SPACE)
} else {
(title_area, Rect::zero())
};
self.title.place(title_area);
self.title_area = title_area;
@ -265,9 +276,11 @@ where
fn paint(&mut self) {
self.pad.paint();
// Scrollbars are painted only with a title
// Scrollbars are painted only with a title and when requested
if self.title.is_some() {
self.scrollbar.paint();
if self.show_scrollbar {
self.scrollbar.paint();
}
self.title.paint();
}
self.buttons.paint();

View File

@ -31,8 +31,7 @@ use crate::{
},
ComponentExt, FormattedText, LineBreaking, Timeout,
},
display::{self},
geometry::Alignment,
display, geometry,
layout::{
obj::{ComponentMsgObj, LayoutObj},
result::{CANCELLED, CONFIRMED, INFO},
@ -641,22 +640,16 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
}
/// General pattern of most tutorial screens.
/// (title, text, btn_layout, btn_actions)
/// (title, text, btn_layout, btn_actions, text_y_offset)
fn tutorial_screen(
title: &'static str,
text: &'static str,
btn_layout: ButtonLayout<StrBuffer>,
btn_actions: ButtonActions,
) -> Page<StrBuffer> {
let mut ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL);
// Add title if present
if !title.is_empty() {
ops = ops.text_bold(title.into()).newline().newline_half()
}
ops = ops.text_normal(text.into());
let formatted = FormattedText::new(ops);
Page::new(btn_layout, btn_actions, formatted)
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL).text_normal(text.into());
let formatted = FormattedText::new(ops).vertically_aligned(geometry::Alignment::Center);
Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
}
extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
@ -674,15 +667,15 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
0 => {
tutorial_screen(
"HELLO",
"Welcome to Trezor.\nPress right to continue.",
ButtonLayout::text_none_arrow("SKIP".into()),
"Welcome to Trezor. Press right to continue.",
ButtonLayout::cancel_none_arrow(),
ButtonActions::last_none_next(),
)
},
1 => {
tutorial_screen(
"",
"Use Trezor by clicking left and right buttons.\n\nContinue right.",
"Use Trezor by\nclicking the left and right buttons.\n\rContinue right.",
ButtonLayout::arrow_none_arrow(),
ButtonActions::prev_none_next(),
)
@ -690,7 +683,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
2 => {
tutorial_screen(
"HOLD TO CONFIRM",
"Press and hold right to approve important operations.",
"Press and hold the right button to\napprove important operations.",
ButtonLayout::arrow_none_htc("HOLD TO CONFIRM".into()),
ButtonActions::prev_none_next(),
)
@ -698,7 +691,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
3 => {
tutorial_screen(
"SCREEN SCROLL",
"Press right to scroll down to read all content when text\ndoesn't fit on one screen. Press left to scroll up.",
"Press right to scroll down to read all content when text doesn't fit on one screen.\n\rPress left to scroll up.",
ButtonLayout::arrow_none_text("CONTINUE".into()),
ButtonActions::prev_none_next(),
)
@ -706,33 +699,24 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
4 => {
tutorial_screen(
"CONFIRM",
"Press both left and right at the same time to confirm.",
"Press both left and right at the same\ntime to confirm.",
ButtonLayout::none_armed_none("CONFIRM".into()),
ButtonActions::prev_next_none(),
)
},
// This page is special
5 => {
let ops = OpTextLayout::<StrBuffer>::new(theme::TEXT_NORMAL)
.newline()
.text_normal("Tutorial complete.".into())
.newline()
.newline()
.alignment(Alignment::Center)
.text_bold("You're ready to\nuse Trezor.".into());
let formatted = FormattedText::new(ops);
Page::new(
ButtonLayout::text_none_text("AGAIN".into(), "FINISH".into()),
tutorial_screen(
"TUTORIAL COMPLETE",
"You're ready to\nuse Trezor.",
ButtonLayout::text_none_text("AGAIN".into(), "CONTINUE".into()),
ButtonActions::beginning_none_confirm(),
formatted,
)
},
6 => {
tutorial_screen(
"SKIP TUTORIAL",
"Are you sure you want to skip the tutorial?",
ButtonLayout::cancel_none_text("SKIP".into()),
"Are you sure you\nwant to skip the tutorial?",
ButtonLayout::arrow_none_text("SKIP".into()),
ButtonActions::beginning_none_cancel(),
)
},
@ -742,7 +726,11 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
let pages = FlowPages::new(get_page, PAGE_COUNT);
let obj = LayoutObj::new(Flow::new(pages))?;
let obj = LayoutObj::new(
Flow::new(pages)
.with_scrollbar(false)
.with_common_title("HELLO".into()),
)?;
Ok(obj.into())
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }

View File

@ -49,7 +49,7 @@ def go_through_tutorial(debug: "DebugLink") -> None:
debug.press_right(wait=True)
debug.press_right(wait=True)
layout = debug.press_middle(wait=True)
assert "Tutorial complete" in layout.text_content()
assert layout.title() == "TUTORIAL COMPLETE"
@pytest.mark.setup_client(uninitialized=True)
@ -66,8 +66,6 @@ def test_tutorial_finish(device_handler: "BackgroundDeviceHandler"):
def test_tutorial_skip(device_handler: "BackgroundDeviceHandler"):
with prepare_tutorial_and_cancel_after_it(device_handler) as debug:
# SKIP
# debug.press_left()
# debug.press_right()
debug.press_left(wait=True)
debug.press_right(wait=True)