mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-19 05:58:09 +00:00
refactor(core/mercury): self-updating footer
This commit enables registering function for updating footer and header based on the content. This eliminates the need to create wrappers around Frame to update them. [no changelog]
This commit is contained in:
parent
7a992a593d
commit
d15ecfb859
@ -259,7 +259,6 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_haptic_feedback__enable;
|
||||
MP_QSTR_haptic_feedback__subtitle;
|
||||
MP_QSTR_haptic_feedback__title;
|
||||
MP_QSTR_highlight_repeated;
|
||||
MP_QSTR_hold;
|
||||
MP_QSTR_hold_danger;
|
||||
MP_QSTR_homescreen__click_to_connect;
|
||||
|
@ -1,101 +0,0 @@
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{swipe_detect::SwipeSettings, Component, SwipeDirection},
|
||||
flow::{Swipable, SwipePage},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
Frame, FrameMsg, InternallySwipable as _, PagedVerticalMenu, SwipeContent,
|
||||
VerticalMenuChoiceMsg,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ConfirmFido {
|
||||
Intro,
|
||||
ChooseCredential,
|
||||
Details,
|
||||
Tap,
|
||||
Menu,
|
||||
}
|
||||
|
||||
/// Wrapper that updates `Footer` content whenever page is changed.
|
||||
pub struct ChooseCredential<F: Fn(usize) -> TString<'static>>(
|
||||
Frame<SwipeContent<SwipePage<PagedVerticalMenu<F>>>>,
|
||||
);
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> ChooseCredential<F> {
|
||||
pub fn new(label_fn: F, num_accounts: usize) -> Self {
|
||||
let content_choose_credential = Frame::left_aligned(
|
||||
TR::fido__title_select_credential.into(),
|
||||
SwipeContent::new(SwipePage::vertical(PagedVerticalMenu::new(
|
||||
num_accounts,
|
||||
label_fn,
|
||||
))),
|
||||
)
|
||||
.with_subtitle(TR::fido__title_for_authentication.into())
|
||||
.with_menu_button()
|
||||
.with_footer_page_hint(
|
||||
TR::fido__more_credentials.into(),
|
||||
TR::buttons__go_back.into(),
|
||||
TR::instructions__swipe_up.into(),
|
||||
TR::instructions__swipe_down.into(),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||
.with_vertical_pages();
|
||||
|
||||
Self(content_choose_credential)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> Component for ChooseCredential<F> {
|
||||
type Msg = FrameMsg<VerticalMenuChoiceMsg>;
|
||||
|
||||
fn place(&mut self, bounds: crate::ui::geometry::Rect) -> crate::ui::geometry::Rect {
|
||||
self.0.place(bounds)
|
||||
}
|
||||
|
||||
fn event(
|
||||
&mut self,
|
||||
ctx: &mut crate::ui::component::EventCtx,
|
||||
event: crate::ui::component::Event,
|
||||
) -> Option<Self::Msg> {
|
||||
let msg = self.0.event(ctx, event);
|
||||
let current_page = self.0.inner().inner().inner().current_page();
|
||||
|
||||
self.0.update_footer_counter(
|
||||
ctx,
|
||||
current_page,
|
||||
Some(self.0.inner().inner().inner().num_pages()),
|
||||
);
|
||||
msg
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.0.paint()
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl crate::ui::shape::Renderer<'s>) {
|
||||
self.0.render(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(usize) -> TString<'static>> Swipable for ChooseCredential<F> {
|
||||
fn get_swipe_config(&self) -> crate::ui::component::swipe_detect::SwipeConfig {
|
||||
self.0.get_swipe_config()
|
||||
}
|
||||
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.0.get_internal_page_count()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F: Fn(usize) -> TString<'static>> crate::trace::Trace for ChooseCredential<F> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
self.0.trace(t)
|
||||
}
|
||||
}
|
@ -85,7 +85,9 @@ pub struct Frame<T> {
|
||||
bounds: Rect,
|
||||
content: T,
|
||||
header: Header,
|
||||
header_update_fn: Option<fn(&T, &mut EventCtx, &mut Header)>,
|
||||
footer: Option<Footer<'static>>,
|
||||
footer_update_fn: Option<fn(&T, &mut EventCtx, &mut Footer)>,
|
||||
swipe: SwipeConfig,
|
||||
internal_page_cnt: usize,
|
||||
horizontal_swipe: HorizontalSwipe,
|
||||
@ -106,7 +108,9 @@ where
|
||||
border: theme::borders(),
|
||||
content,
|
||||
header: Header::new(alignment, title),
|
||||
header_update_fn: None,
|
||||
footer: None,
|
||||
footer_update_fn: None,
|
||||
swipe: SwipeConfig::new(),
|
||||
internal_page_cnt: 1,
|
||||
horizontal_swipe: HorizontalSwipe::new(),
|
||||
@ -217,6 +221,16 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn register_header_update_fn(mut self, f: fn(&T, &mut EventCtx, &mut Header)) -> Self {
|
||||
self.header_update_fn = Some(f);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn register_footer_update_fn(mut self, f: fn(&T, &mut EventCtx, &mut Footer)) -> Self {
|
||||
self.footer_update_fn = Some(f);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_danger(self) -> Self {
|
||||
self.button_styled(theme::button_danger())
|
||||
.title_styled(theme::label_title_danger())
|
||||
@ -230,15 +244,6 @@ where
|
||||
self.header.update_title(ctx, new_title);
|
||||
}
|
||||
|
||||
pub fn update_subtitle(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
new_subtitle: TString<'static>,
|
||||
new_style: Option<TextStyle>,
|
||||
) {
|
||||
self.header.update_subtitle(ctx, new_subtitle, new_style);
|
||||
}
|
||||
|
||||
pub fn update_content<F, R>(&mut self, ctx: &mut EventCtx, update_fn: F) -> R
|
||||
where
|
||||
F: Fn(&mut EventCtx, &mut T) -> R,
|
||||
@ -248,17 +253,6 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
pub fn update_footer_counter(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
current: usize,
|
||||
max: Option<usize>,
|
||||
) {
|
||||
if let Some(footer) = &mut self.footer {
|
||||
footer.update_page_counter(ctx, current, max);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn with_swipe(mut self, dir: SwipeDirection, settings: SwipeSettings) -> Self {
|
||||
self.footer = self.footer.map(|f| f.with_swipe(dir));
|
||||
@ -316,6 +310,16 @@ where
|
||||
return msg;
|
||||
}
|
||||
|
||||
if let Some(header_update_fn) = self.header_update_fn {
|
||||
header_update_fn(&self.content, ctx, &mut self.header);
|
||||
}
|
||||
|
||||
if let Some(footer_update_fn) = self.footer_update_fn {
|
||||
if let Some(footer) = &mut self.footer {
|
||||
footer_update_fn(&self.content, ctx, footer);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@ mod address_details;
|
||||
mod binary_selection;
|
||||
pub mod bl_confirm;
|
||||
mod button;
|
||||
#[cfg(feature = "universal_fw")]
|
||||
mod choose_credential;
|
||||
#[cfg(feature = "translations")]
|
||||
mod coinjoin_progress;
|
||||
mod fido;
|
||||
@ -48,8 +46,6 @@ pub use address_details::AddressDetails;
|
||||
#[cfg(feature = "ui_overlay")]
|
||||
pub use binary_selection::{BinarySelection, BinarySelectionMsg};
|
||||
pub use button::{Button, ButtonContent, ButtonMsg, ButtonStyle, ButtonStyleSheet, IconText};
|
||||
#[cfg(feature = "universal_fw")]
|
||||
pub use choose_credential::ChooseCredential;
|
||||
#[cfg(feature = "translations")]
|
||||
pub use coinjoin_progress::CoinJoinProgress;
|
||||
pub use error::ErrorScreen;
|
||||
|
@ -1,18 +1,14 @@
|
||||
use super::{theme, InternallySwipableContent};
|
||||
use super::theme;
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
base::AttachType,
|
||||
swipe_detect::{SwipeConfig, SwipeSettings},
|
||||
Component, Event, EventCtx, Never, SwipeDirection,
|
||||
base::AttachType, text::TextStyle, Component, Event, EventCtx, Never, SwipeDirection,
|
||||
},
|
||||
event::SwipeEvent,
|
||||
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||
model_mercury::component::{
|
||||
swipe_content::SwipeAttachAnimation, Frame, FrameMsg, InternallySwipable,
|
||||
},
|
||||
model_mercury::component::{swipe_content::SwipeAttachAnimation, InternallySwipable},
|
||||
shape::{self, Renderer},
|
||||
},
|
||||
};
|
||||
@ -25,43 +21,45 @@ type IndexVec = Vec<u8, MAX_WORDS>;
|
||||
/// Component showing mnemonic/share words during backup procedure. Model T3T1
|
||||
/// contains one word per screen. A user is instructed to swipe up/down to see
|
||||
/// next/previous word.
|
||||
/// This is a wrapper around a Frame so that the subtitle and Footer of the
|
||||
/// Frame can be updated based on the index of the word shown. Actual share
|
||||
/// words are rendered within `ShareWordsInner` component,
|
||||
pub struct ShareWords<'a> {
|
||||
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||
subtitle: TString<'static>,
|
||||
frame: Frame<InternallySwipableContent<ShareWordsInner<'a>>>,
|
||||
repeated_indices: Option<IndexVec>,
|
||||
page_index: i16,
|
||||
next_index: i16,
|
||||
/// Area reserved for a shown word from mnemonic/share
|
||||
area_word: Rect,
|
||||
progress: i16,
|
||||
attach_animation: SwipeAttachAnimation,
|
||||
wait_for_attach: bool,
|
||||
repeated_indices: IndexVec,
|
||||
}
|
||||
|
||||
impl<'a> ShareWords<'a> {
|
||||
pub fn new(
|
||||
title: TString<'static>,
|
||||
subtitle: TString<'static>,
|
||||
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||
highlight_repeated: bool,
|
||||
) -> Self {
|
||||
let repeated_indices = if highlight_repeated {
|
||||
Some(Self::find_repeated(share_words.as_slice()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let n_words = share_words.len();
|
||||
const AREA_WORD_HEIGHT: i16 = 91;
|
||||
|
||||
pub fn new(share_words: Vec<TString<'a>, MAX_WORDS>, subtitle: TString<'static>) -> Self {
|
||||
let repeated_indices = Self::find_repeated(share_words.as_slice());
|
||||
Self {
|
||||
share_words,
|
||||
subtitle,
|
||||
frame: Frame::left_aligned(
|
||||
title,
|
||||
InternallySwipableContent::new(ShareWordsInner::new(share_words)),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||
.with_vertical_pages()
|
||||
.with_subtitle(subtitle)
|
||||
.with_footer_counter(TR::instructions__swipe_up.into(), n_words as u8),
|
||||
page_index: 0,
|
||||
next_index: 0,
|
||||
area_word: Rect::zero(),
|
||||
progress: 0,
|
||||
attach_animation: SwipeAttachAnimation::new(),
|
||||
wait_for_attach: false,
|
||||
repeated_indices,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_first_page(&self) -> bool {
|
||||
self.page_index == 0
|
||||
}
|
||||
|
||||
fn is_final_page(&self) -> bool {
|
||||
self.page_index == self.share_words.len() as i16 - 1
|
||||
}
|
||||
|
||||
fn find_repeated(share_words: &[TString]) -> IndexVec {
|
||||
let mut repeated_indices = IndexVec::new();
|
||||
for i in (0..share_words.len()).rev() {
|
||||
@ -73,93 +71,16 @@ impl<'a> ShareWords<'a> {
|
||||
repeated_indices.reverse();
|
||||
repeated_indices
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Component for ShareWords<'a> {
|
||||
type Msg = FrameMsg<Never>;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.frame.place(bounds);
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||
let page_index = self.frame.inner().inner().page_index;
|
||||
if let Some(repeated_indices) = &self.repeated_indices {
|
||||
if repeated_indices.contains(&(page_index as u8)) {
|
||||
let updated_subtitle = TString::from_translation(TR::reset__the_word_is_repeated);
|
||||
self.frame
|
||||
.update_subtitle(ctx, updated_subtitle, Some(theme::TEXT_SUB_GREEN_LIME));
|
||||
} else {
|
||||
self.frame
|
||||
.update_subtitle(ctx, self.subtitle, Some(theme::TEXT_SUB_GREY));
|
||||
}
|
||||
pub fn subtitle(&self) -> (TString<'static>, &'static TextStyle) {
|
||||
if self.repeated_indices.contains(&(self.page_index as u8)) {
|
||||
return (
|
||||
TString::from_translation(TR::reset__the_word_is_repeated),
|
||||
&theme::TEXT_SUB_GREEN_LIME,
|
||||
);
|
||||
}
|
||||
self.frame
|
||||
.update_footer_counter(ctx, page_index as usize, None);
|
||||
self.frame.event(ctx, event)
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
// TODO: remove when ui-t3t1 done
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.frame.render(target);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
impl<'a> crate::ui::flow::Swipable for ShareWords<'a> {
|
||||
fn get_swipe_config(&self) -> SwipeConfig {
|
||||
self.frame.get_swipe_config()
|
||||
}
|
||||
|
||||
fn get_internal_page_count(&self) -> usize {
|
||||
self.frame.get_internal_page_count()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<'a> crate::trace::Trace for ShareWords<'a> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ShareWords");
|
||||
t.child("inner", &self.frame);
|
||||
}
|
||||
}
|
||||
|
||||
struct ShareWordsInner<'a> {
|
||||
share_words: Vec<TString<'a>, MAX_WORDS>,
|
||||
page_index: i16,
|
||||
next_index: i16,
|
||||
/// Area reserved for a shown word from mnemonic/share
|
||||
area_word: Rect,
|
||||
progress: i16,
|
||||
attach_animation: SwipeAttachAnimation,
|
||||
wait_for_attach: bool,
|
||||
}
|
||||
|
||||
impl<'a> ShareWordsInner<'a> {
|
||||
const AREA_WORD_HEIGHT: i16 = 91;
|
||||
|
||||
fn new(share_words: Vec<TString<'a>, MAX_WORDS>) -> Self {
|
||||
Self {
|
||||
share_words,
|
||||
page_index: 0,
|
||||
next_index: 0,
|
||||
area_word: Rect::zero(),
|
||||
progress: 0,
|
||||
attach_animation: SwipeAttachAnimation::new(),
|
||||
wait_for_attach: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_first_page(&self) -> bool {
|
||||
self.page_index == 0
|
||||
}
|
||||
|
||||
fn is_final_page(&self) -> bool {
|
||||
self.page_index == self.share_words.len() as i16 - 1
|
||||
(self.subtitle, &theme::TEXT_SUB_GREY)
|
||||
}
|
||||
|
||||
fn render_word<'s>(&self, word_index: i16, target: &mut impl Renderer<'s>, area: Rect) {
|
||||
@ -200,7 +121,7 @@ impl<'a> ShareWordsInner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Component for ShareWordsInner<'a> {
|
||||
impl<'a> Component for ShareWords<'a> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -210,7 +131,7 @@ impl<'a> Component for ShareWordsInner<'a> {
|
||||
|
||||
self.area_word = Rect::snap(
|
||||
used_area.center(),
|
||||
Offset::new(used_area.width(), ShareWordsInner::AREA_WORD_HEIGHT),
|
||||
Offset::new(used_area.width(), ShareWords::AREA_WORD_HEIGHT),
|
||||
Alignment2D::CENTER,
|
||||
);
|
||||
|
||||
@ -311,7 +232,7 @@ impl<'a> Component for ShareWordsInner<'a> {
|
||||
|
||||
let offset = self
|
||||
.attach_animation
|
||||
.get_offset(t, ShareWordsInner::AREA_WORD_HEIGHT);
|
||||
.get_offset(t, ShareWords::AREA_WORD_HEIGHT);
|
||||
|
||||
target.in_clip(self.area_word, &|target| {
|
||||
target.with_origin(offset, &|target| {
|
||||
@ -324,7 +245,7 @@ impl<'a> Component for ShareWordsInner<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl InternallySwipable for ShareWordsInner<'_> {
|
||||
impl InternallySwipable for ShareWords<'_> {
|
||||
fn current_page(&self) -> usize {
|
||||
self.page_index as usize
|
||||
}
|
||||
@ -335,7 +256,7 @@ impl InternallySwipable for ShareWordsInner<'_> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<'a> crate::trace::Trace for ShareWordsInner<'a> {
|
||||
impl<'a> crate::trace::Trace for ShareWords<'a> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ShareWordsInner");
|
||||
let word = &self.share_words[self.page_index as usize];
|
||||
|
@ -7,11 +7,11 @@ use crate::{
|
||||
component::{
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, Paragraphs},
|
||||
ComponentExt, SwipeDirection,
|
||||
ComponentExt, EventCtx, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::{DecisionBuilder as _, StateChange},
|
||||
FlowMsg, FlowState, SwipeFlow,
|
||||
FlowMsg, FlowState, SwipeFlow, SwipePage,
|
||||
},
|
||||
layout::obj::LayoutObj,
|
||||
},
|
||||
@ -19,8 +19,8 @@ use crate::{
|
||||
|
||||
use super::super::{
|
||||
component::{
|
||||
ChooseCredential, FidoCredential, Frame, FrameMsg, PromptMsg, PromptScreen, SwipeContent,
|
||||
VerticalMenu, VerticalMenuChoiceMsg,
|
||||
FidoCredential, Footer, Frame, FrameMsg, InternallySwipable, PagedVerticalMenu, PromptMsg,
|
||||
PromptScreen, SwipeContent, VerticalMenu, VerticalMenuChoiceMsg,
|
||||
},
|
||||
theme,
|
||||
};
|
||||
@ -85,6 +85,16 @@ pub extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ConfirmFido::new_obj) }
|
||||
}
|
||||
|
||||
fn footer_update_fn(
|
||||
content: &SwipeContent<SwipePage<PagedVerticalMenu<impl Fn(usize) -> TString<'static>>>>,
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
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));
|
||||
}
|
||||
|
||||
impl ConfirmFido {
|
||||
const EXTRA_PADDING: i16 = 6;
|
||||
|
||||
@ -123,11 +133,30 @@ impl ConfirmFido {
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| TString::from_str("-"))
|
||||
};
|
||||
let content_choose_credential =
|
||||
ChooseCredential::new(label_fn, num_accounts).map(|msg| match msg {
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
});
|
||||
|
||||
let content_choose_credential = Frame::left_aligned(
|
||||
TR::fido__title_select_credential.into(),
|
||||
SwipeContent::new(SwipePage::vertical(PagedVerticalMenu::new(
|
||||
num_accounts,
|
||||
label_fn,
|
||||
))),
|
||||
)
|
||||
.with_subtitle(TR::fido__title_for_authentication.into())
|
||||
.with_menu_button()
|
||||
.with_footer_page_hint(
|
||||
TR::fido__more_credentials.into(),
|
||||
TR::buttons__go_back.into(),
|
||||
TR::instructions__swipe_up.into(),
|
||||
TR::instructions__swipe_down.into(),
|
||||
)
|
||||
.register_footer_update_fn(footer_update_fn)
|
||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Right, SwipeSettings::immediate())
|
||||
.with_vertical_pages()
|
||||
.map(|msg| match msg {
|
||||
FrameMsg::Button(_) => Some(FlowMsg::Info),
|
||||
FrameMsg::Content(VerticalMenuChoiceMsg::Selected(i)) => Some(FlowMsg::Choice(i)),
|
||||
});
|
||||
|
||||
let get_account = move || {
|
||||
let current = CRED_SELECTED.load(Ordering::Relaxed);
|
||||
|
@ -8,20 +8,20 @@ use crate::{
|
||||
component::{
|
||||
swipe_detect::SwipeSettings,
|
||||
text::paragraphs::{Paragraph, ParagraphSource, ParagraphVecShort, Paragraphs, VecExt},
|
||||
ButtonRequestExt, ComponentExt, SwipeDirection,
|
||||
ButtonRequestExt, ComponentExt, EventCtx, SwipeDirection,
|
||||
},
|
||||
flow::{
|
||||
base::{DecisionBuilder as _, StateChange},
|
||||
FlowMsg, FlowState, SwipeFlow,
|
||||
},
|
||||
layout::obj::LayoutObj,
|
||||
model_mercury::component::SwipeContent,
|
||||
model_mercury::component::{InternallySwipable, InternallySwipableContent, SwipeContent},
|
||||
},
|
||||
};
|
||||
use heapless::Vec;
|
||||
|
||||
use super::super::{
|
||||
component::{Frame, FrameMsg, PromptScreen, ShareWords},
|
||||
component::{Footer, Frame, FrameMsg, Header, PromptScreen, ShareWords},
|
||||
theme,
|
||||
};
|
||||
|
||||
@ -65,6 +65,24 @@ pub extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs:
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, ShowShareWords::new_obj) }
|
||||
}
|
||||
|
||||
fn header_updating_func(
|
||||
content: &InternallySwipableContent<ShareWords>,
|
||||
ctx: &mut EventCtx,
|
||||
header: &mut Header,
|
||||
) {
|
||||
let (subtitle, subtitle_style) = content.inner().subtitle();
|
||||
header.update_subtitle(ctx, subtitle, Some(*subtitle_style));
|
||||
}
|
||||
fn footer_updating_func(
|
||||
content: &InternallySwipableContent<ShareWords>,
|
||||
ctx: &mut EventCtx,
|
||||
footer: &mut Footer,
|
||||
) {
|
||||
let current_page = content.inner().current_page();
|
||||
let total_pages = content.inner().num_pages();
|
||||
footer.update_page_counter(ctx, current_page, Some(total_pages));
|
||||
}
|
||||
|
||||
impl ShowShareWords {
|
||||
fn new_obj(_args: &[Obj], kwargs: &Map) -> Result<Obj, error::Error> {
|
||||
let title: TString = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
@ -77,7 +95,6 @@ impl ShowShareWords {
|
||||
.and_then(|desc: TString| if desc.is_empty() { None } else { Some(desc) });
|
||||
let text_info: Obj = kwargs.get(Qstr::MP_QSTR_text_info)?;
|
||||
let text_confirm: TString = kwargs.get(Qstr::MP_QSTR_text_confirm)?.try_into()?;
|
||||
let highlight_repeated: bool = kwargs.get(Qstr::MP_QSTR_highlight_repeated)?.try_into()?;
|
||||
let nwords = share_words_vec.len();
|
||||
|
||||
let mut instructions_paragraphs = ParagraphVecShort::new();
|
||||
@ -101,8 +118,19 @@ impl ShowShareWords {
|
||||
.one_button_request(ButtonRequestCode::ResetDevice.with_name("share_words"))
|
||||
.with_pages(move |_| nwords + 2);
|
||||
|
||||
let content_words =
|
||||
ShareWords::new(title, subtitle, share_words_vec, highlight_repeated).map(|_| None);
|
||||
let n_words = share_words_vec.len();
|
||||
let content_words = Frame::left_aligned(
|
||||
title,
|
||||
InternallySwipableContent::new(ShareWords::new(share_words_vec, subtitle)),
|
||||
)
|
||||
.with_swipe(SwipeDirection::Up, SwipeSettings::default())
|
||||
.with_swipe(SwipeDirection::Down, SwipeSettings::default())
|
||||
.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)
|
||||
.register_footer_update_fn(footer_updating_func)
|
||||
.map(|_| None);
|
||||
|
||||
let content_confirm = Frame::left_aligned(
|
||||
text_confirm,
|
||||
|
@ -1524,7 +1524,6 @@ pub static mp_module_trezorui2: Module = obj_module! {
|
||||
/// description: str,
|
||||
/// text_info: Iterable[str],
|
||||
/// text_confirm: str,
|
||||
/// highlight_repeated: bool,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Show wallet backup words preceded by an instruction screen and followed by
|
||||
/// confirmation."""
|
||||
|
@ -403,7 +403,6 @@ def flow_show_share_words(
|
||||
description: str,
|
||||
text_info: Iterable[str],
|
||||
text_confirm: str,
|
||||
highlight_repeated: bool,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Show wallet backup words preceded by an instruction screen and followed by
|
||||
confirmation."""
|
||||
|
@ -23,7 +23,6 @@ async def show_share_words(
|
||||
) -> None:
|
||||
|
||||
title = TR.reset__recovery_wallet_backup_title
|
||||
highlight_repeated = True
|
||||
if share_index is None:
|
||||
subtitle = ""
|
||||
elif group_index is None:
|
||||
@ -52,7 +51,6 @@ async def show_share_words(
|
||||
description=description,
|
||||
text_info=text_info,
|
||||
text_confirm=text_confirm,
|
||||
highlight_repeated=highlight_repeated,
|
||||
)
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user