1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-01 20:08:26 +00:00

feat(core/ui): add page counter to paginated blobs

[no changelog]
This commit is contained in:
Ioan Bizău 2024-11-13 17:53:51 +01:00
parent fc54ad1b77
commit e0e0c11db2
24 changed files with 104 additions and 54 deletions

View File

@ -357,6 +357,7 @@ static void _librust_qstrs(void) {
MP_QSTR_notification;
MP_QSTR_notification_level;
MP_QSTR_page_count;
MP_QSTR_page_counter;
MP_QSTR_page_limit;
MP_QSTR_pages;
MP_QSTR_paint;

View File

@ -150,7 +150,7 @@ where
}
impl<T: Paginate> Paginate for Child<T> {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.component.page_count()
}

View File

@ -22,7 +22,7 @@ pub enum PageMsg<T> {
pub trait Paginate {
/// How many pages of content are there in total?
fn page_count(&mut self) -> usize;
fn page_count(&self) -> usize;
/// Navigate to the given page.
fn change_page(&mut self, active_page: usize);
}

View File

@ -37,6 +37,16 @@ impl FormattedText {
.layout_ops(self.char_offset, Offset::y(self.y_offset), sink)
}
pub(crate) fn layout_content_with_offset(
&self,
sink: &mut dyn LayoutSink,
char_offset: usize,
y_offset: i16,
) -> LayoutFit {
self.op_layout
.layout_ops(char_offset, Offset::y(y_offset), sink)
}
fn align_vertically(&mut self, content_height: i16) {
let bounds_height = self.op_layout.layout.bounds.height();
if content_height >= bounds_height {
@ -53,20 +63,15 @@ impl FormattedText {
// Pagination
impl Paginate for FormattedText {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
let mut page_count = 1; // There's always at least one page.
// Make sure we're starting page counting from the very beginning
// (but remembering the offsets not to change them).
let initial_y_offset = self.y_offset;
let initial_char_offset = self.char_offset;
self.char_offset = 0;
self.y_offset = 0;
let mut char_offset = 0;
// Looping through the content and counting pages
// until we finally fit.
loop {
let fit = self.layout_content(&mut TextNoOp);
let fit = self.layout_content_with_offset(&mut TextNoOp, char_offset, 0);
match fit {
LayoutFit::Fitting { .. } => {
break;
@ -75,15 +80,11 @@ impl Paginate for FormattedText {
processed_chars, ..
} => {
page_count += 1;
self.char_offset += processed_chars;
char_offset += processed_chars;
}
}
}
// Setting the offsets back to the initial values.
self.char_offset = initial_char_offset;
self.y_offset = initial_y_offset;
page_count
}

View File

@ -210,7 +210,7 @@ impl<'a, T> Paginate for Paragraphs<T>
where
T: ParagraphSource<'a>,
{
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
// There's always at least one page.
self.break_pages().count().max(1)
}
@ -374,7 +374,7 @@ struct PageOffset {
}
impl PageOffset {
/// Given an `PageOffset` and a `Rect` area, returns:
/// Given a `PageOffset` and a `Rect` area, returns:
///
/// - The next offset.
/// - Part of `area` that remains free after the current offset is rendered
@ -690,7 +690,7 @@ impl<'a, T> Paginate for Checklist<T>
where
T: ParagraphSource<'a>,
{
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
1
}

View File

@ -47,6 +47,14 @@ impl<T: Component + Paginate> SwipePage<T> {
self.limit = limit;
self
}
pub fn page_count(&self) -> usize {
self.pages
}
pub fn current_page(&self) -> usize {
self.current
}
}
impl<T: Component + Paginate> Component for SwipePage<T> {

View File

@ -117,7 +117,7 @@ impl AddressDetails {
}
impl Paginate for AddressDetails {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.get_internal_page_count()
}

View File

@ -69,11 +69,8 @@ impl<'a> Footer<'a> {
)
}
pub fn with_page_counter(max_pages: u8, instruction: TString<'static>) -> Self {
Self::from_content(FooterContent::PageCounter(PageCounter::new(
max_pages,
instruction,
)))
pub fn with_page_counter(instruction: TString<'static>) -> Self {
Self::from_content(FooterContent::PageCounter(PageCounter::new(instruction)))
}
pub fn with_page_hint(
@ -115,18 +112,18 @@ impl<'a> Footer<'a> {
}
}
pub fn update_page_counter(&mut self, ctx: &mut EventCtx, current: usize, max: Option<usize>) {
pub fn update_page_counter(&mut self, ctx: &mut EventCtx, current: usize, max: usize) {
match &mut self.content {
FooterContent::PageCounter(counter) => {
counter.update_current_page(current);
counter.update_current_page(current, max);
self.swipe_allow_down = counter.is_first_page();
self.swipe_allow_up = counter.is_last_page();
ctx.request_paint();
}
FooterContent::PageHint(page_hint) => {
page_hint.update_current_page(current, max);
self.swipe_allow_down = page_hint.is_first_page();
self.swipe_allow_up = page_hint.is_last_page();
FooterContent::PageHint(hint) => {
hint.update_current_page(current, max);
self.swipe_allow_down = hint.is_first_page();
self.swipe_allow_up = hint.is_last_page();
ctx.request_paint();
}
_ => {
@ -322,16 +319,17 @@ struct PageCounter {
}
impl PageCounter {
fn new(page_max: u8, instruction: TString<'static>) -> Self {
fn new(instruction: TString<'static>) -> Self {
Self {
instruction,
page_curr: 0,
page_max,
page_max: 0,
font: Font::SUB,
}
}
fn update_current_page(&mut self, new_value: usize) {
fn update_current_page(&mut self, new_value: usize, max: usize) {
self.page_max = max as u8;
self.page_curr = (new_value as u8).clamp(0, self.page_max.saturating_sub(1));
}
@ -410,11 +408,9 @@ struct PageHint {
}
impl PageHint {
fn update_current_page(&mut self, current: usize, max: Option<usize>) {
fn update_current_page(&mut self, current: usize, max: usize) {
self.page_num = max as u8;
self.page_curr = (current as u8).clamp(0, self.page_num.saturating_sub(1));
if let Some(max) = max {
self.page_num = max as u8;
}
}
fn update_max_page(&mut self, max: usize) {

View File

@ -195,8 +195,8 @@ where
}
#[inline(never)]
pub fn with_footer_counter(mut self, instruction: TString<'static>, max_value: u8) -> Self {
self.footer = Some(Footer::with_page_counter(max_value, instruction));
pub fn with_footer_counter(mut self, instruction: TString<'static>) -> Self {
self.footer = Some(Footer::with_page_counter(instruction));
self
}

View File

@ -335,7 +335,7 @@ impl<F: Fn(usize) -> TString<'static>> PagedVerticalMenu<F> {
}
impl<F: Fn(usize) -> TString<'static>> Paginate for PagedVerticalMenu<F> {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.num_pages()
}

View File

@ -8,7 +8,7 @@ use crate::{
component::{
swipe_detect::SwipeSettings,
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, VecExt},
Component, ComponentExt, Paginate,
Component, ComponentExt, EventCtx, Paginate,
},
flow::{
base::{Decision, DecisionBuilder as _},
@ -21,7 +21,8 @@ use crate::{
use super::super::{
component::{
Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
Footer, Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent, VerticalMenu,
VerticalMenuChoiceMsg,
},
theme,
};
@ -188,15 +189,17 @@ fn new_confirm_action_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Err
ConfirmActionStrings::new(title, subtitle, None, prompt_screen.then_some(prompt_title)),
hold,
None,
false,
)
}
#[inline(never)]
fn new_confirm_action_uni<T: Component + MaybeTrace + 'static>(
content: T,
fn new_confirm_action_uni<T: Component + Paginate + MaybeTrace + 'static>(
content: SwipeContent<SwipePage<T>>,
menu: ConfirmActionMenu,
strings: ConfirmActionStrings,
hold: bool,
show_page_counter: bool,
) -> Result<Obj, error::Error> {
let (prompt_screen, prompt_pages, flow, page) =
create_flow(strings.title, strings.prompt_screen, hold);
@ -208,6 +211,22 @@ fn new_confirm_action_uni<T: Component + MaybeTrace + 'static>(
.with_menu_button()
.with_footer(TR::instructions__swipe_up.into(), None);
if show_page_counter {
fn footer_update_fn<T: Component + Paginate>(
content: &SwipeContent<SwipePage<T>>,
ctx: &mut EventCtx,
footer: &mut Footer,
) {
let current_page = content.inner().current_page();
let page_count = content.inner().page_count();
footer.update_page_counter(ctx, current_page, page_count);
}
content_intro = content_intro
.with_footer_counter(TR::instructions__swipe_up.into())
.register_footer_update_fn(footer_update_fn::<T>);
}
if let Some(subtitle) = strings.subtitle {
content_intro = content_intro.with_subtitle(subtitle);
}
@ -335,11 +354,13 @@ pub fn new_confirm_action_simple<T: Component + Paginate + MaybeTrace + 'static>
strings: ConfirmActionStrings,
hold: bool,
page_limit: Option<usize>,
show_page_counter: bool,
) -> Result<Obj, error::Error> {
new_confirm_action_uni(
SwipeContent::new(SwipePage::vertical(content).with_limit(page_limit)),
menu,
strings,
hold,
show_page_counter,
)
}

View File

@ -100,7 +100,7 @@ fn footer_update_fn(
) {
let current_page = content.inner().inner().current_page();
let total_pages = content.inner().inner().num_pages();
footer.update_page_counter(ctx, current_page, Some(total_pages));
footer.update_page_counter(ctx, current_page, total_pages);
}
impl ConfirmFido {

View File

@ -147,6 +147,7 @@ impl FlowController for ContinueRecoveryBetweenSharesAdvanced {
pub extern "C" fn new_continue_recovery(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, new_obj) }
}
fn footer_update_fn(
content: &SwipeContent<SwipePage<Paragraphs<ParagraphVecLong>>>,
ctx: &mut EventCtx,
@ -156,7 +157,7 @@ fn footer_update_fn(
// to get total pages instead of using Paginate because it borrows mutably
let current_page = content.inner().inner().current_page();
let total_pages = content.inner().inner().inner().len() / 2; // 2 paragraphs per page
footer.update_page_counter(ctx, current_page, Some(total_pages));
footer.update_page_counter(ctx, current_page, total_pages);
}
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {

View File

@ -81,7 +81,7 @@ fn footer_updating_func(
) {
let current_page = content.inner().current_page();
let total_pages = content.inner().num_pages();
footer.update_page_counter(ctx, current_page, Some(total_pages));
footer.update_page_counter(ctx, current_page, total_pages);
}
impl ShowShareWords {
@ -119,7 +119,6 @@ impl ShowShareWords {
.one_button_request(ButtonRequestCode::ResetDevice.with_name("share_words"))
.with_pages(move |_| nwords + 2);
let n_words = share_words_vec.len();
let content_words = Frame::left_aligned(
title,
InternallySwipableContent::new(ShareWords::new(share_words_vec, subtitle)),
@ -129,7 +128,7 @@ impl ShowShareWords {
.with_vertical_pages()
.with_subtitle(subtitle)
.register_header_update_fn(header_updating_func)
.with_footer_counter(TR::instructions__swipe_up.into(), n_words as u8)
.with_footer_counter(TR::instructions__swipe_up.into())
.register_footer_update_fn(footer_updating_func)
.map(|_| None);

View File

@ -245,6 +245,7 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
ConfirmActionStrings::new(title, None, None, Some(title)),
false,
None,
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -265,6 +266,7 @@ struct ConfirmBlobParams {
hold: bool,
chunkify: bool,
text_mono: bool,
page_counter: bool,
page_limit: Option<usize>,
}
@ -293,6 +295,7 @@ impl ConfirmBlobParams {
hold,
chunkify: false,
text_mono: true,
page_counter: false,
page_limit: None,
}
}
@ -327,6 +330,11 @@ impl ConfirmBlobParams {
self
}
fn with_page_counter(mut self, page_counter: bool) -> Self {
self.page_counter = page_counter;
self
}
fn with_page_limit(mut self, page_limit: Option<usize>) -> Self {
self.page_limit = page_limit;
self
@ -366,6 +374,7 @@ impl ConfirmBlobParams {
),
self.hold,
self.page_limit,
self.page_counter,
)
}
}
@ -402,6 +411,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
let info: bool = kwargs.get_or(Qstr::MP_QSTR_info, true)?;
let hold: bool = kwargs.get_or(Qstr::MP_QSTR_hold, false)?;
let chunkify: bool = kwargs.get_or(Qstr::MP_QSTR_chunkify, false)?;
let page_counter: bool = kwargs.get_or(Qstr::MP_QSTR_page_counter, false)?;
let prompt_screen: bool = kwargs.get_or(Qstr::MP_QSTR_prompt_screen, true)?;
let page_limit: Option<usize> = kwargs
.get(Qstr::MP_QSTR_page_limit)
@ -433,6 +443,7 @@ extern "C" fn new_confirm_blob(n_args: usize, args: *const Obj, kwargs: *mut Map
.with_extra(extra)
.with_info_button(info)
.with_chunkify(chunkify)
.with_page_counter(page_counter)
.with_page_limit(page_limit)
.into_flow()
};
@ -471,6 +482,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
ConfirmActionStrings::new(title, None, None, None),
false,
None,
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -495,6 +507,7 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
ConfirmActionStrings::new(title, None, None, hold.then_some(title)),
hold,
None,
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -527,6 +540,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
),
false,
None,
false,
)
} else {
if !check_homescreen_format(jpeg) {
@ -634,6 +648,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
ConfirmActionStrings::new(title, None, None, Some(title)),
true,
None,
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -923,6 +938,7 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
),
true,
None,
false,
)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
@ -1260,6 +1276,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// info: bool = True,
/// hold: bool = False,
/// chunkify: bool = False,
/// page_counter: bool = False,
/// prompt_screen: bool = False,
/// page_limit: int | None = None,
/// ) -> LayoutObj[UiResult]:

View File

@ -199,7 +199,7 @@ impl Page {
// Pagination
impl Paginate for Page {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.formatted.page_count()
}

View File

@ -87,7 +87,7 @@ impl<T> Paginate for Frame<T>
where
T: Component + Paginate,
{
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.content.page_count()
}

View File

@ -225,7 +225,7 @@ impl Component for ScrollBar {
}
impl Paginate for ScrollBar {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
self.page_count
}

View File

@ -150,7 +150,7 @@ impl<'a> Component for ShareWords<'a> {
}
impl<'a> Paginate for ShareWords<'a> {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
// Not defining the logic here, as we do not want it to be `&mut`.
self.total_page_count()
}

View File

@ -1709,6 +1709,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// info: bool = True,
/// hold: bool = False,
/// chunkify: bool = False,
/// page_counter: bool = False,
/// prompt_screen: bool = False,
/// page_limit: int | None = None,
/// ) -> LayoutObj[UiResult]:

View File

@ -125,7 +125,7 @@ impl AddressDetails {
}
impl Paginate for AddressDetails {
fn page_count(&mut self) -> usize {
fn page_count(&self) -> usize {
let total_xpub_pages: u8 = self.xpub_page_count.iter().copied().sum();
2usize.saturating_add(total_xpub_pages.into())
}

View File

@ -1803,6 +1803,7 @@ pub static mp_module_trezorui2: Module = obj_module! {
/// info: bool = True,
/// hold: bool = False,
/// chunkify: bool = False,
/// page_counter: bool = False,
/// prompt_screen: bool = False,
/// page_limit: int | None = None,
/// ) -> LayoutObj[UiResult]:

View File

@ -68,6 +68,7 @@ def confirm_blob(
info: bool = True,
hold: bool = False,
chunkify: bool = False,
page_counter: bool = False,
prompt_screen: bool = False,
page_limit: int | None = None,
) -> LayoutObj[UiResult]:
@ -640,6 +641,7 @@ def confirm_blob(
info: bool = True,
hold: bool = False,
chunkify: bool = False,
page_counter: bool = False,
prompt_screen: bool = False,
page_limit: int | None = None,
) -> LayoutObj[UiResult]:
@ -1211,6 +1213,7 @@ def confirm_blob(
info: bool = True,
hold: bool = False,
chunkify: bool = False,
page_counter: bool = False,
prompt_screen: bool = False,
page_limit: int | None = None,
) -> LayoutObj[UiResult]:

View File

@ -500,6 +500,7 @@ def confirm_blob(
info=False,
hold=False,
chunkify=chunkify,
page_counter=True,
prompt_screen=False,
)