1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-06-26 01:42:34 +00:00

chore(eckhart): simplify updatable info screen

[no changelog]
This commit is contained in:
Lukas Bielesch 2025-05-20 15:22:53 +02:00 committed by Lukáš Bielesch
parent 13a020202e
commit 468ddaab9d
3 changed files with 80 additions and 35 deletions

View File

@ -42,7 +42,7 @@ pub use qr_screen::{QrMsg, QrScreen};
pub use select_word_screen::{SelectWordMsg, SelectWordScreen};
pub use share_words::{ShareWordsScreen, ShareWordsScreenMsg};
pub use text_screen::{AllowedTextContent, TextScreen, TextScreenMsg};
pub use updatable_info_screen::UpdatableInfoScreen;
pub use updatable_info_screen::{UpdatableInfoScreen, UpdatableInfoScreenMsg};
pub use value_input_screen::{
DurationInput, NumberInput, ValueInput, ValueInputScreen, ValueInputScreenMsg,
};

View File

@ -5,7 +5,7 @@ use crate::{
component::{
swipe_detect::SwipeConfig,
text::paragraphs::{Paragraph, ParagraphSource, Paragraphs},
Component, Event, EventCtx,
Component, Event, EventCtx, PaginateFull,
},
flow::Swipable,
geometry::{LinearPlacement, Rect},
@ -14,16 +14,20 @@ use crate::{
},
};
use super::{theme, Header, TextScreen, TextScreenMsg};
use super::{constant::SCREEN, theme, ActionBar, ActionBarMsg, Header};
pub enum UpdatableInfoScreenMsg {
Close,
}
pub struct UpdatableInfoScreen<F>
where
F: Fn() -> TString<'static>,
{
header: Header,
info_func: F,
paragraphs: Paragraphs<Paragraph<'static>>,
area: Rect,
text_screen: TextScreen<Paragraphs<Paragraph<'static>>>,
paragraph: Paragraphs<Paragraph<'static>>,
action_bar: ActionBar,
}
impl<F> UpdatableInfoScreen<F>
@ -31,59 +35,97 @@ where
F: Fn() -> TString<'static>,
{
pub fn new(info_func: F) -> Self {
let paragraphs = Paragraph::new(&theme::TEXT_REGULAR, TString::empty())
let paragraph = Paragraph::new(&theme::TEXT_REGULAR, TString::empty())
.into_paragraphs()
.with_placement(LinearPlacement::vertical());
let text_screen = create_text_screen(paragraphs.clone());
Self {
header: Header::new(TR::buttons__more_info.into()).with_close_button(),
info_func,
paragraphs,
area: Rect::zero(),
text_screen,
paragraph,
action_bar: ActionBar::new_paginate_only(),
}
}
pub fn with_header(mut self, header: Header) -> Self {
self.header = header;
self
}
fn update_text(&mut self, ctx: &mut EventCtx) {
let text = (self.info_func)();
self.paragraphs.update(text);
self.text_screen = create_text_screen(self.paragraphs.clone());
self.text_screen.place(self.area);
self.paragraph.update(text);
self.update_page(0);
ctx.request_paint();
}
fn update_page(&mut self, page_idx: u16) {
self.paragraph.change_page(page_idx);
self.action_bar.update(self.paragraph.pager());
}
}
impl<F> Component for UpdatableInfoScreen<F>
where
F: Fn() -> TString<'static>,
{
type Msg = TextScreenMsg;
type Msg = UpdatableInfoScreenMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.text_screen.place(bounds);
self.area = bounds;
// assert full screen
debug_assert_eq!(bounds.height(), SCREEN.height());
debug_assert_eq!(bounds.width(), SCREEN.width());
let (header_area, rest) = bounds.split_top(Header::HEADER_HEIGHT);
let (info_area, action_bar_area) = rest.split_bottom(ActionBar::ACTION_BAR_HEIGHT);
self.header.place(header_area);
self.paragraph.place(info_area.inset(theme::SIDE_INSETS));
self.action_bar.place(action_bar_area);
self.update_page(0);
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
// Update page count of the screen
ctx.set_page_count(self.paragraph.pager().total() as usize);
if let Event::Attach(_) = event {
self.update_text(ctx);
}
self.text_screen.event(ctx, event)
self.paragraph.event(ctx, event);
if self.header.event(ctx, event).is_some() {
return Some(UpdatableInfoScreenMsg::Close);
}
if let Some(msg) = self.action_bar.event(ctx, event) {
match msg {
ActionBarMsg::Prev => {
self.update_page(self.paragraph.pager().prev());
return None;
}
ActionBarMsg::Next => {
self.update_page(self.paragraph.pager().next());
return None;
}
_ => {}
}
}
None
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.text_screen.render(target);
self.header.render(target);
self.paragraph.render(target);
self.action_bar.render(target);
}
}
fn create_text_screen(
paragraphs: Paragraphs<Paragraph<'static>>,
) -> TextScreen<Paragraphs<Paragraph<'static>>> {
TextScreen::new(paragraphs)
.with_header(Header::new(TR::buttons__more_info.into()).with_close_button())
}
impl<F: Fn() -> TString<'static>> Swipable for UpdatableInfoScreen<F> {
fn get_pager(&self) -> Pager {
Pager::single_page()
@ -93,6 +135,9 @@ impl<F: Fn() -> TString<'static>> Swipable for UpdatableInfoScreen<F> {
}
}
trait UpdatableTextContent: Component + PaginateFull {}
impl<'a, T> UpdatableTextContent for Paragraphs<T> where T: ParagraphSource<'a> {}
#[cfg(feature = "ui_debug")]
impl<F> crate::trace::Trace for UpdatableInfoScreen<F>
where
@ -100,6 +145,8 @@ where
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("UpdatableInfoScreen");
t.child("screen", &self.text_screen);
t.child("Header", &self.header);
t.child("Content", &self.paragraph);
t.child("ActionBar", &self.action_bar);
}
}

View File

@ -17,9 +17,8 @@ use core::sync::atomic::{AtomicU16, Ordering};
use super::super::{
component::Button,
firmware::{
ActionBar, Header, NumberInput, ShortMenuVec, TextScreenMsg, UpdatableInfoScreen,
ValueInputScreen, ValueInputScreenMsg, VerticalMenu, VerticalMenuScreen,
VerticalMenuScreenMsg,
ActionBar, Header, NumberInput, ShortMenuVec, UpdatableInfoScreen, ValueInputScreen,
ValueInputScreenMsg, VerticalMenu, VerticalMenuScreen, VerticalMenuScreenMsg,
},
theme,
};
@ -104,17 +103,16 @@ pub fn new_request_number(
));
let content_menu = VerticalMenuScreen::new(menu_items)
.with_header(Header::new(TString::empty()).with_close_button())
.with_header(Header::new(title).with_close_button())
.map(move |msg| match msg {
VerticalMenuScreenMsg::Selected(i) => Some(FlowMsg::Choice(i)),
VerticalMenuScreenMsg::Close => Some(FlowMsg::Cancelled),
_ => None,
});
let content_info = UpdatableInfoScreen::new(info_closure).map(|msg| match msg {
TextScreenMsg::Cancelled => Some(FlowMsg::Cancelled),
_ => None,
});
let content_info = UpdatableInfoScreen::new(info_closure)
.with_header(Header::new(title).with_close_button())
.map(|_| Some(FlowMsg::Cancelled));
let mut res = SwipeFlow::new(&RequestNumber::Number)?;
res.add_page(&RequestNumber::Number, content_input)?