mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-27 16:48:09 +00:00
refactor(core/rust) use TString in Label and Button
[no changelog]
This commit is contained in:
parent
ed6aa48726
commit
256adc3567
@ -1,22 +1,22 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::Font,
|
||||
geometry::{Alignment, Insets, Offset, Point, Rect},
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display::Font,
|
||||
geometry::{Alignment, Insets, Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{text::TextStyle, TextLayout};
|
||||
|
||||
pub struct Label<T> {
|
||||
text: T,
|
||||
pub struct Label<'a> {
|
||||
text: TString<'a>,
|
||||
layout: TextLayout,
|
||||
vertical: Alignment,
|
||||
}
|
||||
|
||||
impl<T> Label<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
pub fn new(text: T, align: Alignment, style: TextStyle) -> Self {
|
||||
impl<'a> Label<'a> {
|
||||
pub fn new(text: TString<'a>, align: Alignment, style: TextStyle) -> Self {
|
||||
Self {
|
||||
text,
|
||||
layout: TextLayout::new(style).with_align(align),
|
||||
@ -24,15 +24,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_aligned(text: T, style: TextStyle) -> Self {
|
||||
pub fn left_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::Start, style)
|
||||
}
|
||||
|
||||
pub fn right_aligned(text: T, style: TextStyle) -> Self {
|
||||
pub fn right_aligned(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::End, style)
|
||||
}
|
||||
|
||||
pub fn centered(text: T, style: TextStyle) -> Self {
|
||||
pub fn centered(text: TString<'a>, style: TextStyle) -> Self {
|
||||
Self::new(text, Alignment::Center, style)
|
||||
}
|
||||
|
||||
@ -41,11 +41,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text(&self) -> &T {
|
||||
pub fn text(&self) -> &TString<'a> {
|
||||
&self.text
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, text: T) {
|
||||
pub fn set_text(&mut self, text: TString<'a>) {
|
||||
self.text = text;
|
||||
}
|
||||
|
||||
@ -63,22 +63,22 @@ where
|
||||
|
||||
pub fn max_size(&self) -> Offset {
|
||||
let font = self.font();
|
||||
Offset::new(font.text_width(self.text.as_ref()), font.text_max_height())
|
||||
let width = self.text.map(|c| font.text_width(c));
|
||||
Offset::new(width, font.text_max_height())
|
||||
}
|
||||
|
||||
pub fn text_height(&self, width: i16) -> i16 {
|
||||
let bounds = Rect::from_top_left_and_size(Point::zero(), Offset::new(width, i16::MAX));
|
||||
self.layout
|
||||
.with_bounds(bounds)
|
||||
.fit_text(self.text.as_ref())
|
||||
.height()
|
||||
|
||||
self.text
|
||||
.map(|c| self.layout.with_bounds(bounds).fit_text(c).height())
|
||||
}
|
||||
|
||||
pub fn text_area(&self) -> Rect {
|
||||
// XXX only works on single-line labels
|
||||
assert!(self.layout.bounds.height() <= self.font().text_max_height());
|
||||
let available_width = self.layout.bounds.width();
|
||||
let width = self.font().text_width(self.text.as_ref());
|
||||
let width = self.text.map(|c| self.font().text_width(c));
|
||||
let height = self.font().text_height();
|
||||
let cursor = self.layout.initial_cursor();
|
||||
let baseline = match self.alignment() {
|
||||
@ -90,18 +90,13 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Label<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl Component for Label<'_> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
let height = self
|
||||
.layout
|
||||
.with_bounds(bounds)
|
||||
.fit_text(self.text.as_ref())
|
||||
.height();
|
||||
.text
|
||||
.map(|c| self.layout.with_bounds(bounds).fit_text(c).height());
|
||||
let diff = bounds.height() - height;
|
||||
let insets = match self.vertical {
|
||||
Alignment::Start => Insets::bottom(diff),
|
||||
@ -117,7 +112,7 @@ where
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
self.layout.render_text(self.text.as_ref());
|
||||
self.text.map(|c| self.layout.render_text(c));
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_bounds")]
|
||||
@ -127,12 +122,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Label<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for Label<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Label");
|
||||
t.string("text", self.text.as_ref().into());
|
||||
t.string("text", self.text);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
time::{Duration, Instant},
|
||||
ui::{
|
||||
animation::Animation,
|
||||
@ -21,13 +22,13 @@ enum State {
|
||||
PauseRight,
|
||||
}
|
||||
|
||||
pub struct Marquee<T> {
|
||||
pub struct Marquee {
|
||||
area: Rect,
|
||||
pause_token: Option<TimerToken>,
|
||||
min_offset: i16,
|
||||
max_offset: i16,
|
||||
state: State,
|
||||
text: T,
|
||||
text: TString<'static>,
|
||||
font: Font,
|
||||
fg: Color,
|
||||
bg: Color,
|
||||
@ -35,11 +36,8 @@ pub struct Marquee<T> {
|
||||
pause: Duration,
|
||||
}
|
||||
|
||||
impl<T> Marquee<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
pub fn new(text: T, font: Font, fg: Color, bg: Color) -> Self {
|
||||
impl Marquee {
|
||||
pub fn new(text: TString<'static>, font: Font, fg: Color, bg: Color) -> Self {
|
||||
Self {
|
||||
area: Rect::zero(),
|
||||
pause_token: None,
|
||||
@ -55,7 +53,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, text: T) {
|
||||
pub fn set_text(&mut self, text: TString<'static>) {
|
||||
self.text = text;
|
||||
}
|
||||
|
||||
@ -66,7 +64,7 @@ where
|
||||
}
|
||||
|
||||
if let State::Initial = self.state {
|
||||
let text_width = self.font.text_width(self.text.as_ref());
|
||||
let text_width = self.text.map(|t| self.font.text_width(t));
|
||||
let max_offset = self.area.width() - text_width;
|
||||
|
||||
self.min_offset = 0;
|
||||
@ -122,21 +120,12 @@ where
|
||||
}
|
||||
|
||||
pub fn paint_anim(&mut self, offset: i16) {
|
||||
display::marquee(
|
||||
self.area,
|
||||
self.text.as_ref(),
|
||||
offset,
|
||||
self.font,
|
||||
self.fg,
|
||||
self.bg,
|
||||
);
|
||||
self.text
|
||||
.map(|t| display::marquee(self.area, t, offset, self.font, self.fg, self.bg));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Marquee<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl Component for Marquee {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -228,12 +217,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Marquee<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for Marquee {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Marquee");
|
||||
t.string("text", self.text.as_ref().into());
|
||||
t.string("text", self.text);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
geometry::{Alignment, Alignment2D, Rect},
|
||||
layout::simplified::ReturnToC,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
geometry::{Alignment, Alignment2D, Rect},
|
||||
layout::simplified::ReturnToC,
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
@ -29,14 +32,14 @@ impl ReturnToC for IntroMsg {
|
||||
|
||||
pub struct Intro<'a> {
|
||||
bg: Pad,
|
||||
title: Child<Label<&'a str>>,
|
||||
title: Child<Label<'a>>,
|
||||
buttons: Child<ButtonController>,
|
||||
text: Child<Label<&'a str>>,
|
||||
warn: Option<Child<Label<&'a str>>>,
|
||||
text: Child<Label<'a>>,
|
||||
warn: Option<Child<Label<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Intro<'a> {
|
||||
pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self {
|
||||
pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(Label::centered(title, TEXT_NORMAL).vertically_centered()),
|
||||
@ -46,7 +49,7 @@ impl<'a> Intro<'a> {
|
||||
))),
|
||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
||||
warn: (!fw_ok).then_some(Child::new(
|
||||
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_NORMAL)
|
||||
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_NORMAL)
|
||||
.vertically_centered(),
|
||||
)),
|
||||
}
|
||||
|
@ -126,9 +126,10 @@ impl UIFeaturesBootloader for ModelTRFeatures {
|
||||
unwrap!(reboot_msg.push_str("Reconnect the device"));
|
||||
}
|
||||
|
||||
let title = Label::centered("Firmware installed", TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Firmware installed".into(), TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content = Label::centered(reboot_msg.as_str(), TEXT_NORMAL).vertically_centered();
|
||||
let content =
|
||||
Label::centered(reboot_msg.as_str().into(), TEXT_NORMAL).vertically_centered();
|
||||
|
||||
let mut frame =
|
||||
ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, complete_draw);
|
||||
@ -136,10 +137,10 @@ impl UIFeaturesBootloader for ModelTRFeatures {
|
||||
}
|
||||
|
||||
fn screen_install_fail() {
|
||||
let title = Label::centered("Install failed", TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Install failed".into(), TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
let content = Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame, false);
|
||||
@ -169,44 +170,68 @@ impl UIFeaturesBootloader for ModelTRFeatures {
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
|
||||
let message = Label::left_aligned(version_str.as_str(), TEXT_NORMAL).vertically_centered();
|
||||
let message =
|
||||
Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL).vertically_centered();
|
||||
let fingerprint = Label::left_aligned(
|
||||
fingerprint,
|
||||
fingerprint.into(),
|
||||
TEXT_NORMAL.with_line_breaking(BreakWordsNoHyphen),
|
||||
)
|
||||
.vertically_centered();
|
||||
|
||||
let alert =
|
||||
(!should_keep_seed).then_some(Label::left_aligned("Seed will be erased!", TEXT_NORMAL));
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"Seed will be erased!".into(),
|
||||
TEXT_NORMAL,
|
||||
));
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false)
|
||||
.with_info_screen("FW FINGERPRINT", fingerprint);
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
title_str.into(),
|
||||
message,
|
||||
alert,
|
||||
"INSTALL".into(),
|
||||
false,
|
||||
)
|
||||
.with_info_screen("FW FINGERPRINT".into(), fingerprint);
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_wipe_confirm() -> u32 {
|
||||
let message = Label::left_aligned("Seed and firmware will be erased!", TEXT_NORMAL)
|
||||
let message = Label::left_aligned("Seed and firmware will be erased!".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET", false);
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
"FACTORY RESET".into(),
|
||||
message,
|
||||
None,
|
||||
"RESET".into(),
|
||||
false,
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_confirm() -> u32 {
|
||||
let message =
|
||||
Label::left_aligned("This action cannot be undone!", TEXT_NORMAL).vertically_centered();
|
||||
let message = Label::left_aligned("This action cannot be undone!".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, "UNLOCK BOOTLOADER?", message, None, "UNLOCK", true);
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
"UNLOCK BOOTLOADER?".into(),
|
||||
message,
|
||||
None,
|
||||
"UNLOCK".into(),
|
||||
true,
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
fn screen_unlock_bootloader_success() {
|
||||
let title = Label::centered("Bootloader unlocked", TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Bootloader unlocked".into(), TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect the\ndevice", TEXT_NORMAL).vertically_centered();
|
||||
let content = Label::centered("Please reconnect the\ndevice".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
|
||||
show(&mut frame, false);
|
||||
@ -227,7 +252,11 @@ impl UIFeaturesBootloader for ModelTRFeatures {
|
||||
unwrap!(version_str.push_str("\nby "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok);
|
||||
let mut frame = Intro::new(
|
||||
title_str.as_str().into(),
|
||||
version_str.as_str().into(),
|
||||
fw_ok,
|
||||
);
|
||||
run(&mut frame)
|
||||
}
|
||||
|
||||
@ -268,20 +297,20 @@ impl UIFeaturesBootloader for ModelTRFeatures {
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
let title = Label::centered("Trezor Reset", TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Trezor Reset".into(), TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
let content = Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true);
|
||||
show(&mut frame, false);
|
||||
}
|
||||
|
||||
fn screen_wipe_fail() {
|
||||
let title = Label::centered("Reset failed", TEXT_BOLD).vertically_centered();
|
||||
let title = Label::centered("Reset failed".into(), TEXT_BOLD).vertically_centered();
|
||||
|
||||
let content =
|
||||
Label::centered("Please reconnect\nthe device", TEXT_NORMAL).vertically_centered();
|
||||
let content = Label::centered("Please reconnect\nthe device".into(), TEXT_NORMAL)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_ALERT, title, content, true);
|
||||
show(&mut frame, false);
|
||||
|
@ -23,7 +23,7 @@ const QR_BORDER: i16 = 3;
|
||||
pub struct AddressDetails {
|
||||
qr_code: Qr,
|
||||
details_view: Paragraphs<ParagraphVecShort<'static>>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>, StrBuffer>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
|
||||
xpubs: Vec<(StrBuffer, StrBuffer), MAX_XPUBS>,
|
||||
current_page: usize,
|
||||
current_subpage: usize,
|
||||
@ -149,7 +149,7 @@ impl AddressDetails {
|
||||
|
||||
fn fill_xpub_page(&mut self, ctx: &mut EventCtx) {
|
||||
let i = self.current_page - 2;
|
||||
self.xpub_view.update_title(ctx, self.xpubs[i].0);
|
||||
self.xpub_view.update_title(ctx, self.xpubs[i].0.into());
|
||||
self.xpub_view.update_content(ctx, |p| {
|
||||
p.inner_mut().update(self.xpubs[i].1);
|
||||
p.change_page(0)
|
||||
|
@ -20,14 +20,14 @@ pub enum ConfirmMsg {
|
||||
Confirm = 2,
|
||||
}
|
||||
|
||||
pub struct Confirm<U> {
|
||||
pub struct Confirm<'a> {
|
||||
bg: Pad,
|
||||
bg_color: Color,
|
||||
title: TString<'static>,
|
||||
message: Child<Label<U>>,
|
||||
alert: Option<Label<U>>,
|
||||
info_title: Option<TString<'static>>,
|
||||
info_text: Option<Label<U>>,
|
||||
title: TString<'a>,
|
||||
message: Child<Label<'a>>,
|
||||
alert: Option<Label<'a>>,
|
||||
info_title: Option<TString<'a>>,
|
||||
info_text: Option<Label<'a>>,
|
||||
button_text: TString<'static>,
|
||||
buttons: ButtonController,
|
||||
/// Whether we are on the info screen (optional extra screen)
|
||||
@ -35,25 +35,21 @@ pub struct Confirm<U> {
|
||||
two_btn_confirm: bool,
|
||||
}
|
||||
|
||||
impl<U> Confirm<U>
|
||||
where
|
||||
U: AsRef<str>,
|
||||
{
|
||||
pub fn new<T: Into<TString<'static>>>(
|
||||
impl<'a> Confirm<'a> {
|
||||
pub fn new(
|
||||
bg_color: Color,
|
||||
title: T,
|
||||
message: Label<U>,
|
||||
alert: Option<Label<U>>,
|
||||
button_text: T,
|
||||
title: TString<'a>,
|
||||
message: Label<'a>,
|
||||
alert: Option<Label<'a>>,
|
||||
button_text: TString<'static>,
|
||||
two_btn_confirm: bool,
|
||||
) -> Self {
|
||||
let button_text = button_text.into();
|
||||
let btn_layout =
|
||||
Self::get_button_layout_general(false, button_text, false, two_btn_confirm);
|
||||
Self {
|
||||
bg: Pad::with_background(bg_color).with_clear(),
|
||||
bg_color,
|
||||
title: title.into(),
|
||||
title,
|
||||
message: Child::new(message),
|
||||
alert,
|
||||
info_title: None,
|
||||
@ -66,12 +62,8 @@ where
|
||||
}
|
||||
|
||||
/// Adding optional info screen
|
||||
pub fn with_info_screen<T: Into<TString<'static>>>(
|
||||
mut self,
|
||||
info_title: T,
|
||||
info_text: Label<U>,
|
||||
) -> Self {
|
||||
self.info_title = Some(info_title.into());
|
||||
pub fn with_info_screen(mut self, info_title: TString<'a>, info_text: Label<'a>) -> Self {
|
||||
self.info_title = Some(info_title);
|
||||
self.info_text = Some(info_text);
|
||||
self.buttons = ButtonController::new(self.get_button_layout());
|
||||
self
|
||||
@ -125,10 +117,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> Component for Confirm<U>
|
||||
where
|
||||
U: AsRef<str>,
|
||||
{
|
||||
impl Component for Confirm<'_> {
|
||||
type Msg = ConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -207,7 +196,7 @@ where
|
||||
fn paint(&mut self) {
|
||||
self.bg.paint();
|
||||
|
||||
let display_top_left = |text: TString<'static>| {
|
||||
let display_top_left = |text: TString| {
|
||||
text.map(|t| {
|
||||
display::text_top_left(Point::zero(), t, Font::BOLD, WHITE, self.bg_color)
|
||||
});
|
||||
@ -234,10 +223,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<U> crate::trace::Trace for Confirm<U>
|
||||
where
|
||||
U: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for Confirm<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("BlConfirm");
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::{screen, WIDTH},
|
||||
display,
|
||||
geometry::{Alignment2D, Offset, Point, Rect},
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::{screen, WIDTH},
|
||||
display,
|
||||
geometry::{Alignment2D, Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
@ -13,17 +16,17 @@ use super::super::{
|
||||
const FOOTER_AREA_HEIGHT: i16 = 20;
|
||||
const DIVIDER_POSITION: i16 = 43;
|
||||
|
||||
pub struct ErrorScreen<T> {
|
||||
pub struct ErrorScreen<'a> {
|
||||
bg: Pad,
|
||||
show_icons: bool,
|
||||
title: Child<Label<T>>,
|
||||
message: Child<Label<T>>,
|
||||
footer: Child<Label<T>>,
|
||||
title: Child<Label<'a>>,
|
||||
message: Child<Label<'a>>,
|
||||
footer: Child<Label<'a>>,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ErrorScreen<T> {
|
||||
pub fn new(title: T, message: T, footer: T) -> Self {
|
||||
impl<'a> ErrorScreen<'a> {
|
||||
pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self {
|
||||
let title = Label::centered(title, theme::TEXT_BOLD);
|
||||
let message = Label::centered(message, theme::TEXT_NORMAL).vertically_centered();
|
||||
let footer = Label::centered(footer, theme::TEXT_NORMAL).vertically_centered();
|
||||
@ -39,7 +42,7 @@ impl<T: AsRef<str>> ErrorScreen<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Component for ErrorScreen<T> {
|
||||
impl Component for ErrorScreen<'_> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Pad, Paginate},
|
||||
geometry::Rect,
|
||||
@ -11,17 +11,16 @@ use super::{
|
||||
ButtonControllerMsg, ButtonLayout, ButtonPos, CancelInfoConfirmMsg, FlowPages, Page, ScrollBar,
|
||||
};
|
||||
|
||||
pub struct Flow<F, T>
|
||||
pub struct Flow<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
/// Function to get pages from
|
||||
pages: FlowPages<F, T>,
|
||||
pages: FlowPages<F>,
|
||||
/// Instance of the current Page
|
||||
current_page: Page<T>,
|
||||
current_page: Page,
|
||||
/// Title being shown at the top in bold
|
||||
title: Option<Title<T>>,
|
||||
title: Option<Title>,
|
||||
scrollbar: Child<ScrollBar>,
|
||||
content_area: Rect,
|
||||
title_area: Rect,
|
||||
@ -35,12 +34,11 @@ where
|
||||
ignore_second_button_ms: Option<u32>,
|
||||
}
|
||||
|
||||
impl<F, T> Flow<F, T>
|
||||
impl<F> Flow<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
pub fn new(pages: FlowPages<F, T>) -> Self {
|
||||
pub fn new(pages: FlowPages<F>) -> Self {
|
||||
let current_page = pages.get(0);
|
||||
let title = current_page.title().map(Title::new);
|
||||
Self {
|
||||
@ -64,7 +62,7 @@ where
|
||||
|
||||
/// Adding a common title to all pages. The title will not be colliding
|
||||
/// with the page content, as the content will be offset.
|
||||
pub fn with_common_title(mut self, title: T) -> Self {
|
||||
pub fn with_common_title(mut self, title: TString<'static>) -> Self {
|
||||
self.title = Some(Title::new(title));
|
||||
self
|
||||
}
|
||||
@ -196,10 +194,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Component for Flow<F, T>
|
||||
impl<F> Component for Flow<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
type Msg = CancelInfoConfirmMsg;
|
||||
|
||||
@ -313,10 +310,9 @@ where
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F, T> crate::trace::Trace for Flow<F, T>
|
||||
impl<F> crate::trace::Trace for Flow<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Flow");
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{base::Component, FormattedText, Paginate},
|
||||
geometry::Rect,
|
||||
@ -21,10 +21,9 @@ const MAX_OPS_PER_PAGE: usize = 15;
|
||||
/// have theoretically unlimited number of pages without triggering SO.
|
||||
/// (Currently only the current page is stored on stack - in
|
||||
/// `Flow::current_page`.)
|
||||
pub struct FlowPages<F, T>
|
||||
pub struct FlowPages<F>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page<T>,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
/// Function/closure that will return appropriate page on demand.
|
||||
get_page: F,
|
||||
@ -32,10 +31,9 @@ where
|
||||
page_count: usize,
|
||||
}
|
||||
|
||||
impl<F, T> FlowPages<F, T>
|
||||
impl<F> FlowPages<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
pub fn new(get_page: F, page_count: usize) -> Self {
|
||||
Self {
|
||||
@ -45,7 +43,7 @@ where
|
||||
}
|
||||
|
||||
/// Returns a page on demand on a specified index.
|
||||
pub fn get(&self, page_index: usize) -> Page<T> {
|
||||
pub fn get(&self, page_index: usize) -> Page {
|
||||
(self.get_page)(page_index)
|
||||
}
|
||||
|
||||
@ -74,24 +72,18 @@ where
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Page<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub struct Page {
|
||||
formatted: FormattedText,
|
||||
btn_layout: ButtonLayout,
|
||||
btn_actions: ButtonActions,
|
||||
current_page: usize,
|
||||
page_count: usize,
|
||||
title: Option<T>,
|
||||
title: Option<TString<'static>>,
|
||||
slim_arrows: bool,
|
||||
}
|
||||
|
||||
// For `layout.rs`
|
||||
impl<T> Page<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Page {
|
||||
pub fn new(
|
||||
btn_layout: ButtonLayout,
|
||||
btn_actions: ButtonActions,
|
||||
@ -112,12 +104,9 @@ where
|
||||
}
|
||||
|
||||
// For `flow.rs`
|
||||
impl<T> Page<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Page {
|
||||
/// Adding title.
|
||||
pub fn with_title(mut self, title: T) -> Self {
|
||||
pub fn with_title(mut self, title: TString<'static>) -> Self {
|
||||
self.title = Some(title);
|
||||
self
|
||||
}
|
||||
@ -180,8 +169,8 @@ where
|
||||
self.btn_actions
|
||||
}
|
||||
|
||||
pub fn title(&self) -> Option<T> {
|
||||
self.title.clone()
|
||||
pub fn title(&self) -> Option<TString<'static>> {
|
||||
self.title
|
||||
}
|
||||
|
||||
pub fn has_prev_page(&self) -> bool {
|
||||
@ -208,10 +197,7 @@ where
|
||||
}
|
||||
|
||||
// Pagination
|
||||
impl<T> Paginate for Page<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Paginate for Page {
|
||||
fn page_count(&mut self) -> usize {
|
||||
self.formatted.page_count()
|
||||
}
|
||||
@ -227,10 +213,7 @@ where
|
||||
use crate::ui::component::text::layout::LayoutFit;
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Page<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl crate::trace::Trace for Page {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
use crate::ui::component::text::layout::trace::TraceSink;
|
||||
use core::cell::Cell;
|
||||
@ -238,7 +221,7 @@ where
|
||||
t.component("Page");
|
||||
if let Some(title) = &self.title {
|
||||
// Not calling it "title" as that is already traced by FlowPage
|
||||
t.string("page_title", title.as_ref().into());
|
||||
t.string("page_title", *title);
|
||||
}
|
||||
t.int("active_page", self.current_page as i64);
|
||||
t.int("page_count", self.page_count as i64);
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Paginate},
|
||||
geometry::{Insets, Rect},
|
||||
@ -9,21 +9,19 @@ use crate::{
|
||||
use super::{super::constant, scrollbar::SCROLLBAR_SPACE, theme, title::Title, ScrollBar};
|
||||
|
||||
/// Component for holding another component and displaying a title.
|
||||
pub struct Frame<T, U>
|
||||
pub struct Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
U: StringType,
|
||||
{
|
||||
title: Title<U>,
|
||||
title: Title,
|
||||
content: Child<T>,
|
||||
}
|
||||
|
||||
impl<T, U> Frame<T, U>
|
||||
impl<T> Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
pub fn new(title: U, content: T) -> Self {
|
||||
pub fn new(title: TString<'static>, content: T) -> Self {
|
||||
Self {
|
||||
title: Title::new(title),
|
||||
content: Child::new(content),
|
||||
@ -40,7 +38,7 @@ where
|
||||
self.content.inner()
|
||||
}
|
||||
|
||||
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: U) {
|
||||
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: TString<'static>) {
|
||||
self.title.set_text(ctx, new_title);
|
||||
}
|
||||
|
||||
@ -56,10 +54,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for Frame<T, U>
|
||||
impl<T> Component for Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
type Msg = T::Msg;
|
||||
|
||||
@ -85,10 +82,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Paginate for Frame<T, U>
|
||||
impl<T> Paginate for Frame<T>
|
||||
where
|
||||
T: Component + Paginate,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
fn page_count(&mut self) -> usize {
|
||||
self.content.page_count()
|
||||
@ -106,20 +102,18 @@ pub trait ScrollableContent {
|
||||
|
||||
/// Component for holding another component and displaying a title.
|
||||
/// Also is allocating space for a scrollbar.
|
||||
pub struct ScrollableFrame<T, U>
|
||||
pub struct ScrollableFrame<T>
|
||||
where
|
||||
T: Component + ScrollableContent,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
title: Option<Child<Title<U>>>,
|
||||
title: Option<Child<Title>>,
|
||||
scrollbar: ScrollBar,
|
||||
content: Child<T>,
|
||||
}
|
||||
|
||||
impl<T, U> ScrollableFrame<T, U>
|
||||
impl<T> ScrollableFrame<T>
|
||||
where
|
||||
T: Component + ScrollableContent,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
pub fn new(content: T) -> Self {
|
||||
Self {
|
||||
@ -133,16 +127,15 @@ where
|
||||
self.content.inner()
|
||||
}
|
||||
|
||||
pub fn with_title(mut self, title: U) -> Self {
|
||||
pub fn with_title(mut self, title: TString<'static>) -> Self {
|
||||
self.title = Some(Child::new(Title::new(title)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for ScrollableFrame<T, U>
|
||||
impl<T> Component for ScrollableFrame<T>
|
||||
where
|
||||
T: Component + ScrollableContent,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
type Msg = T::Msg;
|
||||
|
||||
@ -209,10 +202,9 @@ where
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for Frame<T, U>
|
||||
impl<T> crate::trace::Trace for Frame<T>
|
||||
where
|
||||
T: crate::trace::Trace + Component,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Frame");
|
||||
@ -222,10 +214,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for ScrollableFrame<T, U>
|
||||
impl<T> crate::trace::Trace for ScrollableFrame<T>
|
||||
where
|
||||
T: crate::trace::Trace + Component + ScrollableContent,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ScrollableFrame");
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::buffer::StrBuffer,
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
trezorhal::usb::usb_configured,
|
||||
ui::{
|
||||
@ -54,29 +52,27 @@ enum CurrentScreen {
|
||||
Loader,
|
||||
}
|
||||
|
||||
pub struct Homescreen<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
pub struct Homescreen {
|
||||
// TODO label should be a Child in theory, but the homescreen image is not, so it is
|
||||
// always painted, so we need to always paint the label too
|
||||
label: Label<T>,
|
||||
notification: Option<(T, u8)>,
|
||||
label: Label<'static>,
|
||||
notification: Option<(TString<'static>, u8)>,
|
||||
/// Used for HTC functionality to lock device from homescreen
|
||||
invisible_buttons: Child<ButtonController>,
|
||||
/// Holds the loader component
|
||||
loader: Option<Child<ProgressLoader<T>>>,
|
||||
loader: Option<Child<ProgressLoader>>,
|
||||
/// Whether to show the loader or not
|
||||
show_loader: bool,
|
||||
/// Which screen is currently shown
|
||||
current_screen: CurrentScreen,
|
||||
}
|
||||
|
||||
impl<T> Homescreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub fn new(label: T, notification: Option<(T, u8)>, loader_description: Option<T>) -> Self {
|
||||
impl Homescreen {
|
||||
pub fn new(
|
||||
label: TString<'static>,
|
||||
notification: Option<(TString<'static>, u8)>,
|
||||
loader_description: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
// Buttons will not be visible, we only need both left and right to be existing
|
||||
// so we can get the events from them.
|
||||
let invisible_btn_layout = ButtonLayout::text_none_text("".into(), "".into());
|
||||
@ -114,11 +110,11 @@ where
|
||||
.map_translated(|t| display_center(baseline, t, NOTIFICATION_FONT));
|
||||
} else if let Some((notification, _level)) = &self.notification {
|
||||
self.fill_notification_background();
|
||||
display_center(baseline, notification.as_ref(), NOTIFICATION_FONT);
|
||||
notification.map(|c| display_center(baseline, c, NOTIFICATION_FONT));
|
||||
// Painting warning icons in top corners when the text is short enough not to
|
||||
// collide with them
|
||||
let icon_width = NOTIFICATION_ICON.toif.width();
|
||||
let text_width = NOTIFICATION_FONT.text_width(notification.as_ref());
|
||||
let text_width = notification.map(|c| NOTIFICATION_FONT.text_width(c));
|
||||
if AREA.width() >= text_width + (icon_width + 1) * 2 {
|
||||
NOTIFICATION_ICON.draw(
|
||||
AREA.top_left(),
|
||||
@ -169,10 +165,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Homescreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Component for Homescreen {
|
||||
type Msg = ();
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -238,12 +231,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lockscreen<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
label: Child<Label<T>>,
|
||||
instruction: Child<Label<StrBuffer>>,
|
||||
pub struct Lockscreen<'a> {
|
||||
label: Child<Label<'a>>,
|
||||
instruction: Child<Label<'static>>,
|
||||
/// Used for unlocking the device from lockscreen
|
||||
invisible_buttons: Child<ButtonController>,
|
||||
/// Display coinjoin icon?
|
||||
@ -252,11 +242,8 @@ where
|
||||
screensaver: bool,
|
||||
}
|
||||
|
||||
impl<T> Lockscreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub fn new(label: T, bootscreen: bool, coinjoin_authorized: bool) -> Result<Self, Error> {
|
||||
impl<'a> Lockscreen<'a> {
|
||||
pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
|
||||
// Buttons will not be visible, we only need all three of them to be present,
|
||||
// so that even middle-click triggers the event.
|
||||
let invisible_btn_layout = ButtonLayout::arrow_armed_arrow("".into());
|
||||
@ -265,23 +252,17 @@ where
|
||||
} else {
|
||||
TR::homescreen__click_to_unlock
|
||||
};
|
||||
Ok(Lockscreen {
|
||||
Self {
|
||||
label: Child::new(Label::centered(label, theme::TEXT_BIG)),
|
||||
instruction: Child::new(Label::centered(
|
||||
instruction_str.try_into()?,
|
||||
theme::TEXT_NORMAL,
|
||||
)),
|
||||
instruction: Child::new(Label::centered(instruction_str.into(), theme::TEXT_NORMAL)),
|
||||
invisible_buttons: Child::new(ButtonController::new(invisible_btn_layout)),
|
||||
coinjoin_icon: coinjoin_authorized.then_some(theme::ICON_COINJOIN),
|
||||
screensaver: !bootscreen,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Lockscreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Component for Lockscreen<'_> {
|
||||
type Msg = ();
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -322,20 +303,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfirmHomescreen<T, F>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
title: Child<Label<T>>,
|
||||
pub struct ConfirmHomescreen<F> {
|
||||
title: Child<Label<'static>>,
|
||||
buffer_func: F,
|
||||
buttons: Child<ButtonController>,
|
||||
}
|
||||
|
||||
impl<T, F> ConfirmHomescreen<T, F>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub fn new(title: T, buffer_func: F) -> Self {
|
||||
impl<F> ConfirmHomescreen<F> {
|
||||
pub fn new(title: TString<'static>, buffer_func: F) -> Self {
|
||||
let btn_layout = ButtonLayout::cancel_none_text(TR::buttons__change.into());
|
||||
ConfirmHomescreen {
|
||||
title: Child::new(Label::centered(title, theme::TEXT_BOLD)),
|
||||
@ -345,9 +320,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, F> Component for ConfirmHomescreen<T, F>
|
||||
impl<'a, F> Component for ConfirmHomescreen<F>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
F: Fn() -> &'a [u8],
|
||||
{
|
||||
type Msg = CancelConfirmMsg;
|
||||
@ -398,10 +372,7 @@ pub fn check_homescreen_format(toif: &Toif) -> bool {
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Homescreen<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl crate::trace::Trace for Homescreen {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Homescreen");
|
||||
t.child("label", &self.label);
|
||||
@ -409,10 +380,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Lockscreen<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl crate::trace::Trace for Lockscreen<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Lockscreen");
|
||||
t.child("label", &self.label);
|
||||
@ -420,10 +388,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, F> crate::trace::Trace for ConfirmHomescreen<T, F>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl<F> crate::trace::Trace for ConfirmHomescreen<F> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("ConfirmHomescreen");
|
||||
t.child("title", &self.title);
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::{StringType, TString},
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
trezorhal::random,
|
||||
ui::{
|
||||
@ -129,12 +129,12 @@ impl ChoiceFactory for ChoiceFactoryPIN {
|
||||
}
|
||||
|
||||
/// Component for entering a PIN.
|
||||
pub struct PinEntry<T: StringType + Clone> {
|
||||
pub struct PinEntry<'a> {
|
||||
choice_page: ChoicePage<ChoiceFactoryPIN, PinAction>,
|
||||
header_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
||||
pin_line: Child<ChangingTextLine<String<MAX_PIN_LENGTH>>>,
|
||||
prompt: T,
|
||||
subprompt: T,
|
||||
prompt: TString<'a>,
|
||||
subprompt: TString<'a>,
|
||||
/// Whether we already show the "real" prompt (not the warning).
|
||||
showing_real_prompt: bool,
|
||||
show_real_pin: bool,
|
||||
@ -142,26 +142,23 @@ pub struct PinEntry<T: StringType + Clone> {
|
||||
textbox: TextBox<MAX_PIN_LENGTH>,
|
||||
}
|
||||
|
||||
impl<T> PinEntry<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub fn new(prompt: T, subprompt: T) -> Self {
|
||||
impl<'a> PinEntry<'a> {
|
||||
pub fn new(prompt: TString<'a>, subprompt: TString<'a>) -> Self {
|
||||
// When subprompt is not empty, it means that the user has entered bad PIN
|
||||
// before. In this case we show the warning together with the subprompt
|
||||
// at the beginning. (WRONG PIN will be replaced by real prompt after
|
||||
// any button click.)
|
||||
let show_subprompt = !subprompt.as_ref().is_empty();
|
||||
let show_subprompt = !subprompt.is_empty();
|
||||
let (showing_real_prompt, header_line_content, pin_line_content) = if show_subprompt {
|
||||
(
|
||||
false,
|
||||
TR::pin__title_wrong_pin.map_translated(|t| unwrap!(String::try_from(t))),
|
||||
unwrap!(String::try_from(subprompt.as_ref())),
|
||||
subprompt.map(|s| unwrap!(String::try_from(s))),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
true,
|
||||
unwrap!(String::try_from(prompt.as_ref())),
|
||||
prompt.map(|s| unwrap!(String::try_from(s))),
|
||||
unwrap!(String::try_from(EMPTY_PIN_STR)),
|
||||
)
|
||||
};
|
||||
@ -201,10 +198,10 @@ where
|
||||
/// Many possibilities, according to the PIN state.
|
||||
fn update_pin_line(&mut self, ctx: &mut EventCtx) {
|
||||
let mut used_font = Font::BOLD;
|
||||
let pin_line_text = if self.is_empty() && !self.subprompt.as_ref().is_empty() {
|
||||
let pin_line_text = if self.is_empty() && !self.subprompt.is_empty() {
|
||||
// Showing the subprompt in NORMAL font
|
||||
used_font = Font::NORMAL;
|
||||
unwrap!(String::try_from(self.subprompt.as_ref()))
|
||||
self.subprompt.map(|s| unwrap!(String::try_from(s)))
|
||||
} else if self.is_empty() {
|
||||
unwrap!(String::try_from(EMPTY_PIN_STR))
|
||||
} else if self.show_real_pin {
|
||||
@ -234,7 +231,7 @@ where
|
||||
/// Showing the real prompt instead of WRONG PIN
|
||||
fn show_prompt(&mut self, ctx: &mut EventCtx) {
|
||||
self.header_line.mutate(ctx, |ctx, header_line| {
|
||||
header_line.update_text(unwrap!(String::try_from(self.prompt.as_ref())));
|
||||
header_line.update_text(self.prompt.map(|s| unwrap!(String::try_from(s))));
|
||||
header_line.request_complete_repaint(ctx);
|
||||
});
|
||||
}
|
||||
@ -252,10 +249,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for PinEntry<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Component for PinEntry<'_> {
|
||||
type Msg = CancelConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -334,13 +328,10 @@ where
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for PinEntry<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl crate::trace::Trace for PinEntry<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("PinKeyboard");
|
||||
t.string("subprompt", self.subprompt.as_ref().into());
|
||||
t.string("subprompt", self.subprompt);
|
||||
t.string("pin", self.textbox.content().into());
|
||||
t.child("choice_page", &self.choice_page);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::{StringType, TString},
|
||||
strutil::TString,
|
||||
time::{Duration, Instant},
|
||||
ui::{
|
||||
animation::Animation,
|
||||
@ -247,22 +247,16 @@ impl LoaderStyleSheet {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProgressLoader<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
loader: Child<Progress<T>>,
|
||||
pub struct ProgressLoader {
|
||||
loader: Child<Progress>,
|
||||
duration_ms: u32,
|
||||
start_time: Option<Instant>,
|
||||
}
|
||||
|
||||
impl<T> ProgressLoader<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl ProgressLoader {
|
||||
const LOADER_FRAMES_DEFAULT: u32 = 20;
|
||||
|
||||
pub fn new(loader_description: T, duration_ms: u32) -> Self {
|
||||
pub fn new(loader_description: TString<'static>, duration_ms: u32) -> Self {
|
||||
Self {
|
||||
loader: Child::new(
|
||||
Progress::new(false, loader_description).with_icon(theme::ICON_LOCK_SMALL),
|
||||
@ -300,10 +294,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for ProgressLoader<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Component for ProgressLoader {
|
||||
type Msg = LoaderMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::mem;
|
||||
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
paginated::Paginate,
|
||||
@ -21,11 +21,8 @@ const BOTTOM_DESCRIPTION_MARGIN: i16 = 10;
|
||||
const LOADER_Y_OFFSET_TITLE: i16 = -10;
|
||||
const LOADER_Y_OFFSET_NO_TITLE: i16 = -20;
|
||||
|
||||
pub struct Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
title: Option<Child<Label<T>>>,
|
||||
pub struct Progress {
|
||||
title: Option<Child<Label<'static>>>,
|
||||
value: u16,
|
||||
loader_y_offset: i16,
|
||||
indeterminate: bool,
|
||||
@ -34,13 +31,10 @@ where
|
||||
icon: Icon,
|
||||
}
|
||||
|
||||
impl<T> Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl Progress {
|
||||
const AREA: Rect = constant::screen();
|
||||
|
||||
pub fn new(indeterminate: bool, description: T) -> Self {
|
||||
pub fn new(indeterminate: bool, description: TString<'static>) -> Self {
|
||||
Self {
|
||||
title: None,
|
||||
value: 0,
|
||||
@ -54,7 +48,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_title(mut self, title: T) -> Self {
|
||||
pub fn with_title(mut self, title: TString<'static>) -> Self {
|
||||
self.title = Some(Child::new(Label::centered(title, theme::TEXT_BOLD)));
|
||||
self
|
||||
}
|
||||
@ -79,10 +73,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl Component for Progress {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||
@ -95,7 +86,7 @@ where
|
||||
|
||||
let no_title_case = (Rect::zero(), Self::AREA, LOADER_Y_OFFSET_NO_TITLE);
|
||||
let (title, rest, loader_y_offset) = if let Some(self_title) = &self.title {
|
||||
if !self_title.inner().text().as_ref().is_empty() {
|
||||
if !self_title.inner().text().is_empty() {
|
||||
let (title, rest) = Self::AREA.split_top(self_title.inner().max_size().y);
|
||||
(title, rest, LOADER_Y_OFFSET_TITLE)
|
||||
} else {
|
||||
@ -171,10 +162,7 @@ where
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl crate::trace::Trace for Progress {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Progress");
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ pub struct ResultScreen<'a> {
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Icon,
|
||||
message_top: Child<Label<&'static str>>,
|
||||
message_bottom: Child<Label<&'a str>>,
|
||||
message_top: Child<Label<'static>>,
|
||||
message_bottom: Child<Label<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ResultScreen<'a> {
|
||||
@ -26,8 +26,8 @@ impl<'a> ResultScreen<'a> {
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Icon,
|
||||
title: Label<&'static str>,
|
||||
content: Label<&'a str>,
|
||||
title: Label<'static>,
|
||||
content: Label<'a>,
|
||||
complete_draw: bool,
|
||||
) -> Self {
|
||||
let mut instance = Self {
|
||||
@ -49,7 +49,7 @@ impl<'a> ResultScreen<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Component for ResultScreen<'a> {
|
||||
impl Component for ResultScreen<'_> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
time::Instant,
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Marquee, Never},
|
||||
@ -10,24 +10,18 @@ use crate::{
|
||||
|
||||
use super::super::theme;
|
||||
|
||||
pub struct Title<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
pub struct Title {
|
||||
area: Rect,
|
||||
title: T,
|
||||
marquee: Marquee<T>,
|
||||
title: TString<'static>,
|
||||
marquee: Marquee,
|
||||
needs_marquee: bool,
|
||||
centered: bool,
|
||||
}
|
||||
|
||||
impl<T> Title<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
pub fn new(title: T) -> Self {
|
||||
impl Title {
|
||||
pub fn new(title: TString<'static>) -> Self {
|
||||
Self {
|
||||
title: title.clone(),
|
||||
title,
|
||||
marquee: Marquee::new(title, theme::FONT_HEADER, theme::FG, theme::BG),
|
||||
needs_marquee: false,
|
||||
area: Rect::zero(),
|
||||
@ -40,14 +34,14 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_text(&self) -> &str {
|
||||
self.title.as_ref()
|
||||
pub fn get_text(&self) -> TString {
|
||||
self.title
|
||||
}
|
||||
|
||||
pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: T) {
|
||||
self.title = new_text.clone();
|
||||
self.marquee.set_text(new_text.clone());
|
||||
let text_width = theme::FONT_HEADER.text_width(new_text.as_ref());
|
||||
pub fn set_text(&mut self, ctx: &mut EventCtx, new_text: TString<'static>) {
|
||||
self.title = new_text;
|
||||
self.marquee.set_text(new_text);
|
||||
let text_width = new_text.map(|s| theme::FONT_HEADER.text_width(s));
|
||||
self.needs_marquee = text_width > self.area.width();
|
||||
// Resetting the marquee to the beginning and starting it when necessary.
|
||||
self.marquee.reset();
|
||||
@ -57,42 +51,31 @@ where
|
||||
}
|
||||
|
||||
/// Display title/header at the top left of the given area.
|
||||
pub fn paint_header_left(title: &T, area: Rect) {
|
||||
pub fn paint_header_left(title: &TString<'static>, area: Rect) {
|
||||
let text_height = theme::FONT_HEADER.text_height();
|
||||
let title_baseline = area.top_left() + Offset::y(text_height - 1);
|
||||
display::text_left(
|
||||
title_baseline,
|
||||
title.as_ref(),
|
||||
theme::FONT_HEADER,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
title.map(|s| {
|
||||
display::text_left(title_baseline, s, theme::FONT_HEADER, theme::FG, theme::BG)
|
||||
});
|
||||
}
|
||||
|
||||
/// Display title/header centered at the top of the given area.
|
||||
pub fn paint_header_centered(title: &T, area: Rect) {
|
||||
pub fn paint_header_centered(title: &TString<'static>, area: Rect) {
|
||||
let text_height = theme::FONT_HEADER.text_height();
|
||||
let title_baseline = area.top_center() + Offset::y(text_height - 1);
|
||||
display::text_center(
|
||||
title_baseline,
|
||||
title.as_ref(),
|
||||
theme::FONT_HEADER,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
);
|
||||
title.map(|s| {
|
||||
display::text_center(title_baseline, s, theme::FONT_HEADER, theme::FG, theme::BG)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Title<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl Component for Title {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.area = bounds;
|
||||
self.marquee.place(bounds);
|
||||
let width = theme::FONT_HEADER.text_width(self.title.as_ref());
|
||||
let width = self.title.map(|s| theme::FONT_HEADER.text_width(s));
|
||||
self.needs_marquee = width > self.area.width();
|
||||
bounds
|
||||
}
|
||||
@ -121,12 +104,9 @@ where
|
||||
// DEBUG-ONLY SECTION BELOW
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Title<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl crate::trace::Trace for Title {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Title");
|
||||
t.string("text", self.title.as_ref().into());
|
||||
t.string("text", self.title);
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +99,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> ComponentMsgObj for Flow<F, T>
|
||||
impl<F> ComponentMsgObj for Flow<F>
|
||||
where
|
||||
F: Fn(usize) -> Page<T>,
|
||||
T: StringType + Clone,
|
||||
F: Fn(usize) -> Page,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -119,10 +118,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for PinEntry<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl ComponentMsgObj for PinEntry<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
CancelConfirmMsg::Confirmed => self.pin().try_into(),
|
||||
@ -187,56 +183,44 @@ impl ComponentMsgObj for PassphraseEntry {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for Frame<T, U>
|
||||
impl<T> ComponentMsgObj for Frame<T>
|
||||
where
|
||||
T: ComponentMsgObj,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
self.inner().msg_try_into_obj(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for ScrollableFrame<T, U>
|
||||
impl<T> ComponentMsgObj for ScrollableFrame<T>
|
||||
where
|
||||
T: ComponentMsgObj + ScrollableContent,
|
||||
U: StringType + Clone,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
self.inner().msg_try_into_obj(msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl ComponentMsgObj for Progress {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for Homescreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl ComponentMsgObj for Homescreen {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
Ok(CANCELLED.as_obj())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for Lockscreen<T>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
{
|
||||
impl<'a> ComponentMsgObj for Lockscreen<'a> {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
Ok(CANCELLED.as_obj())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, F> ComponentMsgObj for ConfirmHomescreen<T, F>
|
||||
impl<'a, F> ComponentMsgObj for ConfirmHomescreen<F>
|
||||
where
|
||||
T: StringType + Clone,
|
||||
F: Fn() -> &'a [u8],
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
@ -247,10 +231,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<U> ComponentMsgObj for super::component::bl_confirm::Confirm<U>
|
||||
where
|
||||
U: AsRef<str>,
|
||||
{
|
||||
impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
|
||||
@ -290,7 +271,7 @@ fn content_in_button_page<T: Component + Paginate + MaybeTrace + 'static>(
|
||||
|
||||
let mut frame = ScrollableFrame::new(content);
|
||||
if !title.as_ref().is_empty() {
|
||||
frame = frame.with_title(title);
|
||||
frame = frame.with_title(title.into());
|
||||
}
|
||||
let obj = LayoutObj::new(frame)?;
|
||||
|
||||
@ -430,7 +411,7 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
// discarded before returning to micropython.
|
||||
let buffer_func = move || unsafe { unwrap!(get_buffer(data)) };
|
||||
|
||||
let obj = LayoutObj::new(ConfirmHomescreen::new(title, buffer_func))?;
|
||||
let obj = LayoutObj::new(ConfirmHomescreen::new(title.into(), buffer_func))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
|
||||
@ -457,10 +438,6 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
||||
|
||||
extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = move |_args: &[Obj], _kwargs: &Map| {
|
||||
// cached allocated translations that get_page can reuse
|
||||
let tr_title_success: StrBuffer = TR::words__title_success.try_into()?;
|
||||
let tr_title_backup_wallet: StrBuffer = TR::backup__title_backup_wallet.try_into()?;
|
||||
|
||||
let get_page = move |page_index| match page_index {
|
||||
0 => {
|
||||
let btn_layout = ButtonLayout::text_none_arrow_wide(TR::buttons__skip.into());
|
||||
@ -470,7 +447,8 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
.newline()
|
||||
.text_normal(TR::backup__it_should_be_backed_up_now);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(tr_title_success)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
.with_title(TR::words__title_success.into())
|
||||
}
|
||||
1 => {
|
||||
let btn_layout = ButtonLayout::up_arrow_none_text(TR::buttons__back_up.into());
|
||||
@ -478,8 +456,8 @@ extern "C" fn new_confirm_backup(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let ops =
|
||||
OpTextLayout::new(theme::TEXT_NORMAL).text_normal(TR::backup__recover_anytime);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
.with_title(tr_title_backup_wallet)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
.with_title(TR::backup__title_backup_wallet.into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -624,7 +602,7 @@ extern "C" fn new_confirm_output_address(n_args: usize, args: *const Obj, kwargs
|
||||
}
|
||||
ops = ops.text_mono(address);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(address_title)
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(address_title.into())
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 1);
|
||||
|
||||
@ -646,7 +624,7 @@ extern "C" fn new_confirm_output_amount(n_args: usize, args: *const Obj, kwargs:
|
||||
let btn_actions = ButtonActions::cancel_none_confirm();
|
||||
let ops = OpTextLayout::new(theme::TEXT_MONO).text_mono(amount);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title)
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.into())
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 1);
|
||||
|
||||
@ -686,7 +664,7 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
.text_mono(fee_amount);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
}
|
||||
1 => {
|
||||
// Fee rate info
|
||||
@ -748,9 +726,6 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
|
||||
let cancel_cross: bool = kwargs.get_or(Qstr::MP_QSTR_cancel_cross, false)?;
|
||||
let items: Obj = kwargs.get(Qstr::MP_QSTR_items)?;
|
||||
|
||||
// cached allocated translated strings that get_page can reuse
|
||||
let tr_title_fee = TR::confirm_total__title_fee.try_into()?;
|
||||
|
||||
let get_page = move |page_index| {
|
||||
match page_index {
|
||||
0 => {
|
||||
@ -771,7 +746,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
|
||||
.text_mono(fee_value);
|
||||
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title)
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(amount_title.into())
|
||||
}
|
||||
1 => {
|
||||
// Other information
|
||||
@ -794,7 +769,7 @@ extern "C" fn new_altcoin_tx_summary(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
.with_title(tr_title_fee)
|
||||
.with_title(TR::confirm_total__title_fee.into())
|
||||
.with_slim_arrows()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@ -829,7 +804,7 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
};
|
||||
let ops = OpTextLayout::new(style).text_mono(address);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(title.into())
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 1);
|
||||
|
||||
@ -842,11 +817,11 @@ 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, text_y_offset)
|
||||
fn tutorial_screen(
|
||||
title: StrBuffer,
|
||||
title: TString<'static>,
|
||||
text: TR,
|
||||
btn_layout: ButtonLayout,
|
||||
btn_actions: ButtonActions,
|
||||
) -> Page<StrBuffer> {
|
||||
) -> Page {
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::new(btn_layout, btn_actions, formatted).with_title(title)
|
||||
@ -856,15 +831,6 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
||||
let block = |_args: &[Obj], _kwargs: &Map| {
|
||||
const PAGE_COUNT: usize = 7;
|
||||
|
||||
// cached allocated translated strings that get_page can reuse
|
||||
let tr_title_hello: StrBuffer = TR::tutorial__title_hello.try_into()?;
|
||||
let tr_hold_to_confirm: StrBuffer = TR::buttons__hold_to_confirm.try_into()?;
|
||||
let tr_title_screen_scroll: StrBuffer = TR::tutorial__title_screen_scroll.try_into()?;
|
||||
let tr_confirm: StrBuffer = TR::buttons__confirm.try_into()?;
|
||||
let tr_title_tutorial_complete: StrBuffer =
|
||||
TR::tutorial__title_tutorial_complete.try_into()?;
|
||||
let tr_title_skip: StrBuffer = TR::tutorial__title_skip.try_into()?;
|
||||
|
||||
let get_page = move |page_index| {
|
||||
// Lazy-loaded list of screens to show, with custom content,
|
||||
// buttons and actions triggered by these buttons.
|
||||
@ -874,7 +840,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
||||
match page_index {
|
||||
// title, text, btn_layout, btn_actions
|
||||
0 => tutorial_screen(
|
||||
tr_title_hello,
|
||||
TR::tutorial__title_hello.into(),
|
||||
TR::tutorial__welcome_press_right,
|
||||
ButtonLayout::cancel_none_arrow(),
|
||||
ButtonActions::last_none_next(),
|
||||
@ -886,25 +852,25 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
2 => tutorial_screen(
|
||||
tr_hold_to_confirm,
|
||||
TR::buttons__hold_to_confirm.into(),
|
||||
TR::tutorial__press_and_hold,
|
||||
ButtonLayout::arrow_none_htc(TR::buttons__hold_to_confirm.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
3 => tutorial_screen(
|
||||
tr_title_screen_scroll,
|
||||
TR::tutorial__title_screen_scroll.into(),
|
||||
TR::tutorial__scroll_down,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__continue.into()),
|
||||
ButtonActions::prev_none_next(),
|
||||
),
|
||||
4 => tutorial_screen(
|
||||
tr_confirm,
|
||||
TR::buttons__confirm.into(),
|
||||
TR::tutorial__middle_click,
|
||||
ButtonLayout::none_armed_none(TR::buttons__confirm.into()),
|
||||
ButtonActions::none_next_none(),
|
||||
),
|
||||
5 => tutorial_screen(
|
||||
tr_title_tutorial_complete,
|
||||
TR::tutorial__title_tutorial_complete.into(),
|
||||
TR::tutorial__ready_to_use,
|
||||
ButtonLayout::text_none_text(
|
||||
TR::buttons__again.into(),
|
||||
@ -913,7 +879,7 @@ extern "C" fn tutorial(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj
|
||||
ButtonActions::beginning_none_confirm(),
|
||||
),
|
||||
6 => tutorial_screen(
|
||||
tr_title_skip,
|
||||
TR::tutorial__title_skip.into(),
|
||||
TR::tutorial__sure_you_want_skip,
|
||||
ButtonLayout::arrow_none_text(TR::buttons__skip.into()),
|
||||
ButtonActions::beginning_none_cancel(),
|
||||
@ -1021,11 +987,11 @@ extern "C" fn new_multiple_pages_texts(n_args: usize, args: *const Obj, kwargs:
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL).text_normal(text);
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
};
|
||||
|
||||
let pages = FlowPages::new(get_page, page_count);
|
||||
let obj = LayoutObj::new(Flow::new(pages).with_common_title(title))?;
|
||||
let obj = LayoutObj::new(Flow::new(pages).with_common_title(title.into()))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1080,14 +1046,14 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
.text_bold(account);
|
||||
let formatted = FormattedText::new(ops);
|
||||
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
};
|
||||
|
||||
let pages = FlowPages::new(get_page, page_count);
|
||||
// Returning the page index in case of confirmation.
|
||||
let obj = LayoutObj::new(
|
||||
Flow::new(pages)
|
||||
.with_common_title(title)
|
||||
.with_common_title(title.into())
|
||||
.with_return_confirmed_index(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
@ -1115,7 +1081,7 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
ops = ops.text_normal(description);
|
||||
}
|
||||
let formatted = FormattedText::new(ops).vertically_centered();
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 1);
|
||||
let obj = LayoutObj::new(Flow::new(pages))?;
|
||||
@ -1132,7 +1098,7 @@ extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -
|
||||
let time_ms: u32 = kwargs.get_or(Qstr::MP_QSTR_time_ms, 0)?;
|
||||
|
||||
let content = Frame::new(
|
||||
title,
|
||||
title.into(),
|
||||
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
|
||||
);
|
||||
let obj = if time_ms == 0 {
|
||||
@ -1189,7 +1155,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
.newline()
|
||||
.text_bold(TR::addr_mismatch__support_url);
|
||||
let formatted = FormattedText::new(ops);
|
||||
Page::<StrBuffer>::new(btn_layout, btn_actions, formatted)
|
||||
Page::new(btn_layout, btn_actions, formatted)
|
||||
};
|
||||
let pages = FlowPages::new(get_page, 1);
|
||||
|
||||
@ -1222,7 +1188,7 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
}
|
||||
|
||||
let obj = LayoutObj::new(Frame::new(
|
||||
title,
|
||||
title.into(),
|
||||
ShowMore::<Paragraphs<ParagraphVecShort>>::new(
|
||||
paragraphs.into_paragraphs(),
|
||||
verb_cancel,
|
||||
@ -1291,7 +1257,7 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||
let subprompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_subprompt)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(PinEntry::new(prompt, subprompt))?;
|
||||
let obj = LayoutObj::new(PinEntry::new(prompt.into(), subprompt.into()))?;
|
||||
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1302,7 +1268,9 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(Frame::new(prompt, PassphraseEntry::new()).with_title_centered())?;
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(prompt.into(), PassphraseEntry::new()).with_title_centered(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1310,13 +1278,13 @@ extern "C" fn new_request_passphrase(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||
let block = |_args: &[Obj], kwargs: &Map| {
|
||||
let prompt: StrBuffer = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||
let prompt: TString = kwargs.get(Qstr::MP_QSTR_prompt)?.try_into()?;
|
||||
let prefill_word: StrBuffer = kwargs.get(Qstr::MP_QSTR_prefill_word)?.try_into()?;
|
||||
let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(
|
||||
prompt,
|
||||
prompt.into(),
|
||||
WordlistEntry::prefilled_word(
|
||||
prefill_word.as_ref(),
|
||||
WordlistType::Bip39,
|
||||
@ -1338,7 +1306,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(
|
||||
prompt,
|
||||
prompt.into(),
|
||||
WordlistEntry::prefilled_word(
|
||||
prefill_word.as_ref(),
|
||||
WordlistType::Slip39,
|
||||
@ -1363,7 +1331,7 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
// Returning the index of the selected word, not the word itself
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(
|
||||
description,
|
||||
description.into(),
|
||||
SimpleChoice::new(words, false)
|
||||
.with_show_incomplete()
|
||||
.with_return_index(),
|
||||
@ -1402,7 +1370,8 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let count: u32 = kwargs.get(Qstr::MP_QSTR_count)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(title, NumberInput::new(min_count, max_count, count)).with_title_centered(),
|
||||
Frame::new(title.into(), NumberInput::new(min_count, max_count, count))
|
||||
.with_title_centered(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1497,7 +1466,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
.collect();
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Frame::new(title, SimpleChoice::new(choices, false)).with_title_centered(),
|
||||
Frame::new(title.into(), SimpleChoice::new(choices, false)).with_title_centered(),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1542,9 +1511,9 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
.and_then(Obj::try_into_option)
|
||||
.unwrap_or(None);
|
||||
|
||||
let mut progress = Progress::new(indeterminate, description);
|
||||
let mut progress = Progress::new(indeterminate, description.into());
|
||||
if let Some(title) = title {
|
||||
progress = progress.with_title(title);
|
||||
progress = progress.with_title(title.into());
|
||||
};
|
||||
|
||||
// Description updates are received as &str and we need to provide a way to
|
||||
@ -1590,9 +1559,13 @@ extern "C" fn new_show_homescreen(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
|
||||
let hold: bool = kwargs.get(Qstr::MP_QSTR_hold)?.try_into()?;
|
||||
|
||||
let notification = notification.map(|w| (w, notification_level));
|
||||
let notification = notification.map(|w| (w.into(), notification_level));
|
||||
let loader_description = hold.then_some("Locking the device...".into());
|
||||
let obj = LayoutObj::new(Homescreen::new(label, notification, loader_description))?;
|
||||
let obj = LayoutObj::new(Homescreen::new(
|
||||
label.into(),
|
||||
notification,
|
||||
loader_description,
|
||||
))?;
|
||||
if skip_first_paint {
|
||||
obj.skip_first_paint();
|
||||
}
|
||||
@ -1611,7 +1584,11 @@ extern "C" fn new_show_lockscreen(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let coinjoin_authorized: bool = kwargs.get_or(Qstr::MP_QSTR_coinjoin_authorized, false)?;
|
||||
let skip_first_paint: bool = kwargs.get(Qstr::MP_QSTR_skip_first_paint)?.try_into()?;
|
||||
|
||||
let obj = LayoutObj::new(Lockscreen::new(label, bootscreen, coinjoin_authorized)?)?;
|
||||
let obj = LayoutObj::new(Lockscreen::new(
|
||||
label.into(),
|
||||
bootscreen,
|
||||
coinjoin_authorized,
|
||||
))?;
|
||||
if skip_first_paint {
|
||||
obj.skip_first_paint();
|
||||
}
|
||||
@ -1631,16 +1608,27 @@ extern "C" fn new_confirm_firmware_update(
|
||||
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
|
||||
|
||||
let title = TR::firmware_update__title;
|
||||
let message = Label::left_aligned(description, theme::TEXT_NORMAL).vertically_centered();
|
||||
let message =
|
||||
Label::left_aligned(description.into(), theme::TEXT_NORMAL).vertically_centered();
|
||||
let fingerprint = Label::left_aligned(
|
||||
fingerprint,
|
||||
fingerprint.into(),
|
||||
theme::TEXT_NORMAL.with_line_breaking(LineBreaking::BreakWordsNoHyphen),
|
||||
)
|
||||
.vertically_centered();
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Confirm::new(theme::BG, title, message, None, TR::buttons__install, false)
|
||||
.with_info_screen(TR::firmware_update__title_fingerprint, fingerprint),
|
||||
Confirm::new(
|
||||
theme::BG,
|
||||
title.into(),
|
||||
message,
|
||||
None,
|
||||
TR::buttons__install.as_tstring(),
|
||||
false,
|
||||
)
|
||||
.with_info_screen(
|
||||
TR::firmware_update__title_fingerprint.as_tstring(),
|
||||
fingerprint,
|
||||
),
|
||||
)?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
|
@ -1,31 +1,11 @@
|
||||
#[cfg(feature = "micropython")]
|
||||
use crate::micropython::buffer::StrBuffer;
|
||||
use crate::ui::{
|
||||
component::base::Component, constant::screen, display, model_tr::component::WelcomeScreen,
|
||||
};
|
||||
|
||||
use super::{component::ErrorScreen, constant};
|
||||
|
||||
#[cfg(not(feature = "micropython"))]
|
||||
// SAFETY: Actually safe but see below
|
||||
unsafe fn get_str(text: &str) -> &str {
|
||||
text
|
||||
}
|
||||
#[cfg(feature = "micropython")]
|
||||
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not
|
||||
// escape the lifetime of the original &str.
|
||||
unsafe fn get_str(text: &str) -> StrBuffer {
|
||||
unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) }
|
||||
}
|
||||
|
||||
pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) {
|
||||
// SAFETY: these will get placed into `frame` which does not outlive this
|
||||
// function
|
||||
let title = unsafe { get_str(title) };
|
||||
let msg = unsafe { get_str(msg) };
|
||||
let footer = unsafe { get_str(footer) };
|
||||
|
||||
let mut frame = ErrorScreen::new(title, msg, footer);
|
||||
let mut frame = ErrorScreen::new(title.into(), msg.into(), footer.into());
|
||||
frame.place(constant::screen());
|
||||
frame.paint();
|
||||
}
|
||||
|
@ -1,14 +1,17 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
constant::screen,
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Point, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg::Clicked},
|
||||
constant::WIDTH,
|
||||
theme::bootloader::{
|
||||
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT,
|
||||
CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_WARNING, TITLE_AREA,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
constant::screen,
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Point, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg::Clicked},
|
||||
constant::WIDTH,
|
||||
theme::bootloader::{
|
||||
button_bld, button_bld_menu, text_title, BLD_BG, BUTTON_AREA_START, BUTTON_HEIGHT,
|
||||
CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, TEXT_NORMAL, TEXT_WARNING, TITLE_AREA,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -22,15 +25,15 @@ pub enum IntroMsg {
|
||||
|
||||
pub struct Intro<'a> {
|
||||
bg: Pad,
|
||||
title: Child<Label<&'a str>>,
|
||||
menu: Child<Button<&'static str>>,
|
||||
host: Child<Button<&'static str>>,
|
||||
text: Child<Label<&'a str>>,
|
||||
warn: Option<Child<Label<&'a str>>>,
|
||||
title: Child<Label<'a>>,
|
||||
menu: Child<Button>,
|
||||
host: Child<Button>,
|
||||
text: Child<Label<'a>>,
|
||||
warn: Option<Child<Label<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Intro<'a> {
|
||||
pub fn new(title: &'a str, content: &'a str, fw_ok: bool) -> Self {
|
||||
pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()),
|
||||
@ -39,10 +42,10 @@ impl<'a> Intro<'a> {
|
||||
.styled(button_bld_menu())
|
||||
.with_expanded_touch_area(Insets::uniform(13)),
|
||||
),
|
||||
host: Child::new(Button::with_text("INSTALL FIRMWARE").styled(button_bld())),
|
||||
host: Child::new(Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld())),
|
||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
||||
warn: (!fw_ok).then_some(Child::new(
|
||||
Label::new("FIRMWARE CORRUPTED", Alignment::Start, TEXT_WARNING)
|
||||
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
|
||||
.vertically_centered(),
|
||||
)),
|
||||
}
|
||||
|
@ -29,10 +29,10 @@ pub enum MenuMsg {
|
||||
|
||||
pub struct Menu {
|
||||
bg: Pad,
|
||||
title: Child<Label<&'static str>>,
|
||||
close: Child<Button<&'static str>>,
|
||||
reboot: Child<Button<&'static str>>,
|
||||
reset: Child<Button<&'static str>>,
|
||||
title: Child<Label<'static>>,
|
||||
close: Child<Button>,
|
||||
reboot: Child<Button>,
|
||||
reset: Child<Button>,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
@ -43,7 +43,7 @@ impl Menu {
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(
|
||||
Label::left_aligned("BOOTLOADER", text_title(BLD_BG)).vertically_centered(),
|
||||
Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG)).vertically_centered(),
|
||||
),
|
||||
close: Child::new(
|
||||
Button::with_icon(Icon::new(X32))
|
||||
|
@ -74,8 +74,8 @@ impl ModelTTFeatures {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_FW_INSTALL,
|
||||
Icon::new(CHECK40),
|
||||
"Firmware installed\nsuccessfully",
|
||||
Label::centered(msg, RESULT_FW_INSTALL.title_style()).vertically_centered(),
|
||||
"Firmware installed\nsuccessfully".into(),
|
||||
Label::centered(msg.into(), RESULT_FW_INSTALL.title_style()).vertically_centered(),
|
||||
complete_draw,
|
||||
);
|
||||
show(&mut frame, complete_draw);
|
||||
@ -85,8 +85,8 @@ impl ModelTTFeatures {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_INITIAL,
|
||||
Icon::new(CHECK40),
|
||||
"Firmware installed\nsuccessfully",
|
||||
Label::centered(msg, RESULT_INITIAL.title_style()).vertically_centered(),
|
||||
"Firmware installed\nsuccessfully".into(),
|
||||
Label::centered(msg.into(), RESULT_INITIAL.title_style()).vertically_centered(),
|
||||
complete_draw,
|
||||
);
|
||||
show(&mut frame, complete_draw);
|
||||
@ -133,8 +133,8 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_FW_INSTALL,
|
||||
Icon::new(WARNING40),
|
||||
"Firmware installation was not successful",
|
||||
Label::centered(RECONNECT_MESSAGE, RESULT_FW_INSTALL.title_style())
|
||||
"Firmware installation was not successful".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
@ -164,14 +164,16 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
} else {
|
||||
"DOWNGRADE FW"
|
||||
};
|
||||
let title = Label::left_aligned(title_str, TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(version_str.as_ref(), TEXT_NORMAL);
|
||||
let alert =
|
||||
(!should_keep_seed).then_some(Label::left_aligned("SEED WILL BE ERASED!", TEXT_BOLD));
|
||||
let title = Label::left_aligned(title_str.into(), TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(version_str.as_str().into(), TEXT_NORMAL);
|
||||
let alert = (!should_keep_seed).then_some(Label::left_aligned(
|
||||
"SEED WILL BE ERASED!".into(),
|
||||
TEXT_BOLD,
|
||||
));
|
||||
|
||||
let (left, right) = if should_keep_seed {
|
||||
let l = Button::with_text("CANCEL").styled(button_bld());
|
||||
let r = Button::with_text("INSTALL").styled(button_confirm());
|
||||
let l = Button::with_text("CANCEL".into()).styled(button_bld());
|
||||
let r = Button::with_text("INSTALL".into()).styled(button_confirm());
|
||||
(l, r)
|
||||
} else {
|
||||
let l = Button::with_icon(Icon::new(X24)).styled(button_bld());
|
||||
@ -180,7 +182,11 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
};
|
||||
|
||||
let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg)
|
||||
.with_info("FW FINGERPRINT", fingerprint, button_bld_menu());
|
||||
.with_info(
|
||||
"FW FINGERPRINT".into(),
|
||||
fingerprint.into(),
|
||||
button_bld_menu(),
|
||||
);
|
||||
|
||||
if let Some(alert) = alert {
|
||||
frame = frame.with_alert(alert);
|
||||
@ -193,13 +199,13 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
let icon = Icon::new(FIRE40);
|
||||
|
||||
let msg = Label::centered(
|
||||
"Are you sure you want to factory reset the device?",
|
||||
"Are you sure you want to factory reset the device?".into(),
|
||||
TEXT_WIPE_NORMAL,
|
||||
);
|
||||
let alert = Label::centered("SEED AND FIRMWARE\nWILL BE ERASED!", TEXT_WIPE_BOLD);
|
||||
let alert = Label::centered("SEED AND FIRMWARE\nWILL BE ERASED!".into(), TEXT_WIPE_BOLD);
|
||||
|
||||
let right = Button::with_text("RESET").styled(button_wipe_confirm());
|
||||
let left = Button::with_text("CANCEL").styled(button_wipe_cancel());
|
||||
let right = Button::with_text("RESET".into()).styled(button_wipe_confirm());
|
||||
let left = Button::with_text("CANCEL".into()).styled(button_wipe_cancel());
|
||||
|
||||
let mut frame = Confirm::new(BLD_WIPE_COLOR, left, right, ConfirmTitle::Icon(icon), msg)
|
||||
.with_alert(alert);
|
||||
@ -230,7 +236,11 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
unwrap!(version_str.push_str("\nby "));
|
||||
unwrap!(version_str.push_str(vendor));
|
||||
|
||||
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok);
|
||||
let mut frame = Intro::new(
|
||||
title_str.as_str().into(),
|
||||
version_str.as_str().into(),
|
||||
fw_ok,
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
}
|
||||
@ -288,8 +298,9 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_WIPE,
|
||||
Icon::new(CHECK40),
|
||||
"Trezor reset\nsuccessfully",
|
||||
Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(),
|
||||
"Trezor reset\nsuccessfully".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
@ -299,8 +310,9 @@ impl UIFeaturesBootloader for ModelTTFeatures {
|
||||
let mut frame = ResultScreen::new(
|
||||
&RESULT_WIPE,
|
||||
Icon::new(WARNING40),
|
||||
"Trezor reset was\nnot successful",
|
||||
Label::centered(RECONNECT_MESSAGE, RESULT_WIPE.title_style()).vertically_centered(),
|
||||
"Trezor reset was\nnot successful".into(),
|
||||
Label::centered(RECONNECT_MESSAGE.into(), RESULT_WIPE.title_style())
|
||||
.vertically_centered(),
|
||||
true,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
|
@ -19,9 +19,9 @@ use super::{theme, Frame, FrameMsg};
|
||||
const MAX_XPUBS: usize = 16;
|
||||
|
||||
pub struct AddressDetails<T> {
|
||||
qr_code: Frame<Qr, T>,
|
||||
details: Frame<Paragraphs<ParagraphVecShort<'static>>, T>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>, T>,
|
||||
qr_code: Frame<Qr>,
|
||||
details: Frame<Paragraphs<ParagraphVecShort<'static>>>,
|
||||
xpub_view: Frame<Paragraphs<Paragraph<'static>>>,
|
||||
xpubs: Vec<(T, T), MAX_XPUBS>,
|
||||
xpub_page_count: Vec<u8, MAX_XPUBS>,
|
||||
current_page: usize,
|
||||
@ -60,14 +60,14 @@ where
|
||||
let result = Self {
|
||||
qr_code: Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
qr_title,
|
||||
qr_title.into(),
|
||||
Qr::new(qr_address, case_sensitive)?.with_border(7),
|
||||
)
|
||||
.with_cancel_button()
|
||||
.with_border(theme::borders_horizontal_scroll()),
|
||||
details: Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
details_title,
|
||||
details_title.into(),
|
||||
para.into_paragraphs(),
|
||||
)
|
||||
.with_cancel_button()
|
||||
@ -101,7 +101,7 @@ where
|
||||
// repaint after page change so we can use a dummy context here.
|
||||
let mut dummy_ctx = EventCtx::new();
|
||||
self.xpub_view
|
||||
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone());
|
||||
.update_title(&mut dummy_ctx, self.xpubs[i].0.clone().into());
|
||||
self.xpub_view.update_content(&mut dummy_ctx, |p| {
|
||||
p.inner_mut().update(self.xpubs[i].1.clone());
|
||||
let npages = p.page_count();
|
||||
|
@ -1,18 +1,22 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
||||
constant,
|
||||
constant::screen,
|
||||
display::{Color, Icon},
|
||||
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg::Clicked, ButtonStyleSheet},
|
||||
constant::WIDTH,
|
||||
theme::{
|
||||
bootloader::{
|
||||
text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING,
|
||||
CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32, TITLE_AREA, X32,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, Label, Pad},
|
||||
constant,
|
||||
constant::screen,
|
||||
display::{Color, Icon},
|
||||
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg::Clicked, ButtonStyleSheet},
|
||||
constant::WIDTH,
|
||||
theme::{
|
||||
bootloader::{
|
||||
text_fingerprint, text_title, BUTTON_AREA_START, BUTTON_HEIGHT,
|
||||
CONTENT_PADDING, CORNER_BUTTON_AREA, CORNER_BUTTON_TOUCH_EXPANSION, INFO32,
|
||||
TITLE_AREA, X32,
|
||||
},
|
||||
WHITE,
|
||||
},
|
||||
WHITE,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -31,41 +35,38 @@ pub enum ConfirmMsg {
|
||||
Confirm = 2,
|
||||
}
|
||||
|
||||
pub enum ConfirmTitle<T> {
|
||||
Text(Label<T>),
|
||||
pub enum ConfirmTitle {
|
||||
Text(Label<'static>),
|
||||
Icon(Icon),
|
||||
}
|
||||
|
||||
pub struct ConfirmInfo<T> {
|
||||
pub title: Child<Label<T>>,
|
||||
pub text: Child<Label<T>>,
|
||||
pub info_button: Child<Button<&'static str>>,
|
||||
pub close_button: Child<Button<&'static str>>,
|
||||
pub struct ConfirmInfo<'a> {
|
||||
pub title: Child<Label<'a>>,
|
||||
pub text: Child<Label<'a>>,
|
||||
pub info_button: Child<Button>,
|
||||
pub close_button: Child<Button>,
|
||||
}
|
||||
|
||||
pub struct Confirm<T> {
|
||||
pub struct Confirm<'a> {
|
||||
bg: Pad,
|
||||
content_pad: Pad,
|
||||
bg_color: Color,
|
||||
title: ConfirmTitle<T>,
|
||||
message: Child<Label<T>>,
|
||||
alert: Option<Child<Label<T>>>,
|
||||
left_button: Child<Button<T>>,
|
||||
right_button: Child<Button<T>>,
|
||||
info: Option<ConfirmInfo<T>>,
|
||||
title: ConfirmTitle,
|
||||
message: Child<Label<'a>>,
|
||||
alert: Option<Child<Label<'static>>>,
|
||||
left_button: Child<Button>,
|
||||
right_button: Child<Button>,
|
||||
info: Option<ConfirmInfo<'a>>,
|
||||
show_info: bool,
|
||||
}
|
||||
|
||||
impl<T> Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl<'a> Confirm<'a> {
|
||||
pub fn new(
|
||||
bg_color: Color,
|
||||
left_button: Button<T>,
|
||||
right_button: Button<T>,
|
||||
title: ConfirmTitle<T>,
|
||||
message: Label<T>,
|
||||
left_button: Button,
|
||||
right_button: Button,
|
||||
title: ConfirmTitle,
|
||||
message: Label<'a>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(bg_color).with_clear(),
|
||||
@ -81,12 +82,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_alert(mut self, alert: Label<T>) -> Self {
|
||||
pub fn with_alert(mut self, alert: Label<'static>) -> Self {
|
||||
self.alert = Some(Child::new(alert.vertically_centered()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_info(mut self, title: T, text: T, menu_button: ButtonStyleSheet) -> Self {
|
||||
pub fn with_info(
|
||||
mut self,
|
||||
title: TString<'a>,
|
||||
text: TString<'a>,
|
||||
menu_button: ButtonStyleSheet,
|
||||
) -> Self {
|
||||
self.info = Some(ConfirmInfo {
|
||||
title: Child::new(
|
||||
Label::left_aligned(title, text_title(self.bg_color)).vertically_centered(),
|
||||
@ -109,10 +115,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl Component for Confirm<'_> {
|
||||
type Msg = ConfirmMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -242,10 +245,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for Confirm<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("BlConfirm");
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#[cfg(feature = "haptic")]
|
||||
use crate::trezorhal::haptic::{play, HapticEffect};
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
time::Duration,
|
||||
ui::{
|
||||
component::{
|
||||
@ -21,22 +22,22 @@ pub enum ButtonMsg {
|
||||
LongPressed,
|
||||
}
|
||||
|
||||
pub struct Button<T> {
|
||||
pub struct Button {
|
||||
area: Rect,
|
||||
touch_expand: Option<Insets>,
|
||||
content: ButtonContent<T>,
|
||||
content: ButtonContent,
|
||||
styles: ButtonStyleSheet,
|
||||
state: State,
|
||||
long_press: Option<Duration>,
|
||||
long_timer: Option<TimerToken>,
|
||||
}
|
||||
|
||||
impl<T> Button<T> {
|
||||
impl Button {
|
||||
/// Offsets the baseline of the button text either up (negative) or down
|
||||
/// (positive).
|
||||
pub const BASELINE_OFFSET: i16 = -2;
|
||||
|
||||
pub const fn new(content: ButtonContent<T>) -> Self {
|
||||
pub const fn new(content: ButtonContent) -> Self {
|
||||
Self {
|
||||
content,
|
||||
area: Rect::zero(),
|
||||
@ -48,7 +49,7 @@ impl<T> Button<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn with_text(text: T) -> Self {
|
||||
pub const fn with_text(text: TString<'static>) -> Self {
|
||||
Self::new(ButtonContent::Text(text))
|
||||
}
|
||||
|
||||
@ -117,17 +118,14 @@ impl<T> Button<T> {
|
||||
matches!(self.state, State::Disabled)
|
||||
}
|
||||
|
||||
pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent<T>)
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
pub fn set_content(&mut self, ctx: &mut EventCtx, content: ButtonContent) {
|
||||
if self.content != content {
|
||||
self.content = content;
|
||||
ctx.request_paint();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &ButtonContent<T> {
|
||||
pub fn content(&self) -> &ButtonContent {
|
||||
&self.content
|
||||
}
|
||||
|
||||
@ -189,26 +187,24 @@ impl<T> Button<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paint_content(&self, style: &ButtonStyle)
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
pub fn paint_content(&self, style: &ButtonStyle) {
|
||||
match &self.content {
|
||||
ButtonContent::Empty => {}
|
||||
ButtonContent::Text(text) => {
|
||||
let text = text.as_ref();
|
||||
let width = style.font.text_width(text);
|
||||
let width = text.map(|c| style.font.text_width(c));
|
||||
let height = style.font.text_height();
|
||||
let start_of_baseline = self.area.center()
|
||||
+ Offset::new(-width / 2, height / 2)
|
||||
+ Offset::y(Self::BASELINE_OFFSET);
|
||||
display::text_left(
|
||||
start_of_baseline,
|
||||
text,
|
||||
style.font,
|
||||
style.text_color,
|
||||
style.button_color,
|
||||
);
|
||||
text.map(|text| {
|
||||
display::text_left(
|
||||
start_of_baseline,
|
||||
text,
|
||||
style.font,
|
||||
style.text_color,
|
||||
style.button_color,
|
||||
);
|
||||
});
|
||||
}
|
||||
ButtonContent::Icon(icon) => {
|
||||
icon.draw(
|
||||
@ -231,10 +227,7 @@ impl<T> Button<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Button<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl Component for Button {
|
||||
type Msg = ButtonMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -327,15 +320,12 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Button<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for Button {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Button");
|
||||
match &self.content {
|
||||
ButtonContent::Empty => {}
|
||||
ButtonContent::Text(text) => t.string("text", text.as_ref().into()),
|
||||
ButtonContent::Text(text) => t.string("text", *text),
|
||||
ButtonContent::Icon(_) => t.bool("icon", true),
|
||||
ButtonContent::IconAndText(content) => {
|
||||
t.string("text", content.text.into());
|
||||
@ -355,9 +345,9 @@ enum State {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ButtonContent<T> {
|
||||
pub enum ButtonContent {
|
||||
Empty,
|
||||
Text(T),
|
||||
Text(TString<'static>),
|
||||
Icon(Icon),
|
||||
IconAndText(IconText),
|
||||
IconBlend(Icon, Icon, Offset),
|
||||
@ -381,19 +371,15 @@ pub struct ButtonStyle {
|
||||
pub border_width: i16,
|
||||
}
|
||||
|
||||
impl<T> Button<T> {
|
||||
impl Button {
|
||||
pub fn cancel_confirm(
|
||||
left: Button<T>,
|
||||
right: Button<T>,
|
||||
left: Button,
|
||||
right: Button,
|
||||
left_is_small: bool,
|
||||
) -> CancelConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
> {
|
||||
let width = if left_is_small {
|
||||
theme::BUTTON_WIDTH
|
||||
} else {
|
||||
@ -412,21 +398,17 @@ impl<T> Button<T> {
|
||||
}
|
||||
|
||||
pub fn cancel_confirm_text(
|
||||
left: Option<T>,
|
||||
right: Option<T>,
|
||||
left: Option<TString<'static>>,
|
||||
right: Option<TString<'static>>,
|
||||
) -> CancelConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
> {
|
||||
let left_is_small: bool;
|
||||
|
||||
let left = if let Some(verb) = left {
|
||||
left_is_small = verb.as_ref().len() <= 4;
|
||||
if verb.as_ref() == "^" {
|
||||
left_is_small = verb.len() <= 4;
|
||||
if verb == "^".into() {
|
||||
Button::with_icon(theme::ICON_UP)
|
||||
} else {
|
||||
Button::with_text(verb)
|
||||
@ -444,17 +426,13 @@ impl<T> Button<T> {
|
||||
}
|
||||
|
||||
pub fn cancel_info_confirm(
|
||||
confirm: T,
|
||||
info: T,
|
||||
confirm: TString<'static>,
|
||||
info: TString<'static>,
|
||||
) -> CancelInfoConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<CancelInfoConfirmMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
> {
|
||||
let right = Button::with_text(confirm)
|
||||
.styled(theme::button_confirm())
|
||||
.map(|msg| {
|
||||
@ -479,16 +457,12 @@ impl<T> Button<T> {
|
||||
}
|
||||
|
||||
pub fn select_word(
|
||||
words: [T; 3],
|
||||
words: [TString<'static>; 3],
|
||||
) -> CancelInfoConfirm<
|
||||
T,
|
||||
impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
|
||||
impl Fn(ButtonMsg) -> Option<SelectWordMsg>,
|
||||
>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
> {
|
||||
let btn = move |i, word| {
|
||||
Button::with_text(word)
|
||||
.styled(theme::button_pin())
|
||||
@ -521,11 +495,10 @@ pub enum CancelConfirmMsg {
|
||||
Confirmed,
|
||||
}
|
||||
|
||||
type CancelInfoConfirm<T, F0, F1, F2> = FixedHeightBar<
|
||||
Split<MsgMap<Button<T>, F0>, Split<MsgMap<Button<T>, F1>, MsgMap<Button<T>, F2>>>,
|
||||
>;
|
||||
type CancelInfoConfirm<F0, F1, F2> =
|
||||
FixedHeightBar<Split<MsgMap<Button, F0>, Split<MsgMap<Button, F1>, MsgMap<Button, F2>>>>;
|
||||
|
||||
type CancelConfirm<T, F0, F1> = FixedHeightBar<Split<MsgMap<Button<T>, F0>, MsgMap<Button<T>, F1>>>;
|
||||
type CancelConfirm<F0, F1> = FixedHeightBar<Split<MsgMap<Button, F0>, MsgMap<Button, F1>>>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CancelInfoConfirmMsg {
|
||||
|
@ -3,7 +3,7 @@ use core::mem;
|
||||
use crate::{
|
||||
error::Error,
|
||||
maybe_trace::MaybeTrace,
|
||||
micropython::buffer::StrBuffer,
|
||||
strutil::TString,
|
||||
translations::TR,
|
||||
ui::{
|
||||
component::{
|
||||
@ -25,50 +25,44 @@ const LOADER_INNER: i16 = 28;
|
||||
const LOADER_OFFSET: i16 = -34;
|
||||
const LOADER_SPEED: u16 = 5;
|
||||
|
||||
pub struct CoinJoinProgress<T, U> {
|
||||
pub struct CoinJoinProgress<U> {
|
||||
value: u16,
|
||||
indeterminate: bool,
|
||||
content: Child<Frame<Split<Empty, U>, StrBuffer>>,
|
||||
content: Child<Frame<Split<Empty, U>>>,
|
||||
// Label is not a child since circular loader paints large black rectangle which overlaps it.
|
||||
// To work around this, draw label every time loader is drawn.
|
||||
label: Label<T>,
|
||||
label: Label<'static>,
|
||||
}
|
||||
|
||||
impl<T, U> CoinJoinProgress<T, U>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl<U> CoinJoinProgress<U> {
|
||||
pub fn new(
|
||||
text: T,
|
||||
text: TString<'static>,
|
||||
indeterminate: bool,
|
||||
) -> Result<CoinJoinProgress<T, impl Component<Msg = Never> + MaybeTrace>, Error>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
) -> Result<CoinJoinProgress<impl Component<Msg = Never> + MaybeTrace>, Error> {
|
||||
let style = theme::label_coinjoin_progress();
|
||||
let label = Label::centered(
|
||||
TryInto::<StrBuffer>::try_into(TR::coinjoin__title_do_not_disconnect)?,
|
||||
style,
|
||||
)
|
||||
.vertically_centered();
|
||||
let label = Label::centered(TR::coinjoin__title_do_not_disconnect.into(), style)
|
||||
.vertically_centered();
|
||||
let bg = painter::rect_painter(style.background_color, theme::BG);
|
||||
let inner = (bg, label);
|
||||
CoinJoinProgress::with_background(text, inner, indeterminate)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> CoinJoinProgress<T, U>
|
||||
impl<U> CoinJoinProgress<U>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
U: Component<Msg = Never>,
|
||||
{
|
||||
pub fn with_background(text: T, inner: U, indeterminate: bool) -> Result<Self, Error> {
|
||||
pub fn with_background(
|
||||
text: TString<'static>,
|
||||
inner: U,
|
||||
indeterminate: bool,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
value: 0,
|
||||
indeterminate,
|
||||
content: Frame::centered(
|
||||
theme::label_title(),
|
||||
TR::coinjoin__title_progress.try_into()?,
|
||||
TR::coinjoin__title_progress.into(),
|
||||
Split::bottom(RECTANGLE_HEIGHT, 0, Empty, inner),
|
||||
)
|
||||
.into_child(),
|
||||
@ -77,9 +71,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for CoinJoinProgress<T, U>
|
||||
impl<U> Component for CoinJoinProgress<U>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
U: Component<Msg = Never>,
|
||||
{
|
||||
type Msg = Never;
|
||||
@ -132,9 +125,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for CoinJoinProgress<T, U>
|
||||
impl<U> crate::trace::Trace for CoinJoinProgress<U>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
U: Component + crate::trace::Trace,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
|
@ -1,7 +1,10 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::screen,
|
||||
geometry::{Alignment2D, Point, Rect},
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::screen,
|
||||
geometry::{Alignment2D, Point, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::ui::model_tt::{
|
||||
@ -19,15 +22,15 @@ const STYLE: &ResultStyle = &crate::ui::model_tt::theme::bootloader::RESULT_WIPE
|
||||
#[cfg(not(feature = "bootloader"))]
|
||||
const STYLE: &ResultStyle = &super::theme::RESULT_ERROR;
|
||||
|
||||
pub struct ErrorScreen<'a, T> {
|
||||
pub struct ErrorScreen<'a> {
|
||||
bg: Pad,
|
||||
title: Child<Label<T>>,
|
||||
message: Child<Label<T>>,
|
||||
footer: Child<ResultFooter<'a, T>>,
|
||||
title: Child<Label<'a>>,
|
||||
message: Child<Label<'a>>,
|
||||
footer: Child<ResultFooter<'a>>,
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> ErrorScreen<'_, T> {
|
||||
pub fn new(title: T, message: T, footer: T) -> Self {
|
||||
impl<'a> ErrorScreen<'a> {
|
||||
pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self {
|
||||
let title = Label::centered(title, STYLE.title_style());
|
||||
let message = Label::centered(message, STYLE.message_style());
|
||||
let footer = ResultFooter::new(
|
||||
@ -44,7 +47,7 @@ impl<T: AsRef<str>> ErrorScreen<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Component for ErrorScreen<'_, T> {
|
||||
impl<'a> Component for ErrorScreen<'a> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||
@ -62,7 +65,7 @@ impl<T: AsRef<str>> Component for ErrorScreen<'_, T> {
|
||||
);
|
||||
self.message.place(message_area);
|
||||
|
||||
let (_, bottom_area) = ResultFooter::<T>::split_bounds();
|
||||
let (_, bottom_area) = ResultFooter::split_bounds();
|
||||
self.footer.place(bottom_area);
|
||||
|
||||
screen()
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::ui::{
|
||||
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
||||
display,
|
||||
geometry::{Insets, Rect},
|
||||
model_tt::component::{
|
||||
fido_icons::get_fido_icon_data,
|
||||
swipe::{Swipe, SwipeDirection},
|
||||
theme, ScrollBar,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
||||
display,
|
||||
geometry::{Insets, Rect},
|
||||
model_tt::component::{
|
||||
fido_icons::get_fido_icon_data,
|
||||
swipe::{Swipe, SwipeDirection},
|
||||
theme, ScrollBar,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -23,10 +26,10 @@ pub enum FidoMsg {
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
pub struct FidoConfirm<F: Fn(usize) -> T, T, U> {
|
||||
pub struct FidoConfirm<F: Fn(usize) -> TString<'static>, U> {
|
||||
page_swipe: Swipe,
|
||||
app_name: Label<T>,
|
||||
account_name: Label<T>,
|
||||
app_name: Label<'static>,
|
||||
account_name: Label<'static>,
|
||||
icon: Child<Image>,
|
||||
/// Function/closure that will return appropriate page on demand.
|
||||
get_account: F,
|
||||
@ -35,20 +38,19 @@ pub struct FidoConfirm<F: Fn(usize) -> T, T, U> {
|
||||
controls: U,
|
||||
}
|
||||
|
||||
impl<F, T, U> FidoConfirm<F, T, U>
|
||||
impl<F, U> FidoConfirm<F, U>
|
||||
where
|
||||
F: Fn(usize) -> T,
|
||||
T: AsRef<str> + From<&'static str>,
|
||||
F: Fn(usize) -> TString<'static>,
|
||||
U: Component<Msg = CancelConfirmMsg>,
|
||||
{
|
||||
pub fn new(
|
||||
app_name: T,
|
||||
app_name: TString<'static>,
|
||||
get_account: F,
|
||||
page_count: usize,
|
||||
icon_name: Option<T>,
|
||||
icon_name: Option<TString<'static>>,
|
||||
controls: U,
|
||||
) -> Self {
|
||||
let icon_data = get_fido_icon_data(icon_name.as_ref());
|
||||
let icon_data = get_fido_icon_data(icon_name);
|
||||
|
||||
// Preparing scrollbar and setting its page-count.
|
||||
let mut scrollbar = ScrollBar::horizontal();
|
||||
@ -116,10 +118,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, U> Component for FidoConfirm<F, T, U>
|
||||
impl<F, U> Component for FidoConfirm<F, U>
|
||||
where
|
||||
F: Fn(usize) -> T,
|
||||
T: AsRef<str> + From<&'static str>,
|
||||
F: Fn(usize) -> TString<'static>,
|
||||
U: Component<Msg = CancelConfirmMsg>,
|
||||
{
|
||||
type Msg = FidoMsg;
|
||||
@ -198,8 +199,8 @@ where
|
||||
// Account name is optional.
|
||||
// Showing it only if it differs from app name.
|
||||
// (Dummy requests usually have some text as both app_name and account_name.)
|
||||
let account_name = self.account_name.text().as_ref();
|
||||
let app_name = self.app_name.text().as_ref();
|
||||
let account_name = self.account_name.text();
|
||||
let app_name = self.app_name.text();
|
||||
if !account_name.is_empty() && account_name != app_name {
|
||||
self.account_name.paint();
|
||||
}
|
||||
@ -219,9 +220,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<F, T, U> crate::trace::Trace for FidoConfirm<F, T, U>
|
||||
impl<F, T> crate::trace::Trace for FidoConfirm<F, T>
|
||||
where
|
||||
F: Fn(usize) -> T,
|
||||
F: Fn(usize) -> TString<'static>,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("FidoConfirm");
|
||||
|
@ -3,6 +3,9 @@
|
||||
//! do not edit manually!
|
||||
|
||||
|
||||
use crate::strutil::TString;
|
||||
|
||||
|
||||
const ICON_APPLE: &[u8] = include_res!("model_tt/res/fido/icon_apple.toif");
|
||||
const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif");
|
||||
const ICON_BINANCE: &[u8] = include_res!("model_tt/res/fido/icon_binance.toif");
|
||||
@ -39,9 +42,9 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"
|
||||
/// Translates icon name into its data.
|
||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||
/// supplied.
|
||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
||||
pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
|
||||
if let Some(icon_name) = icon_name {
|
||||
match icon_name.as_ref() {
|
||||
icon_name.map(|c| match c {
|
||||
"apple" => ICON_APPLE,
|
||||
"aws" => ICON_AWS,
|
||||
"binance" => ICON_BINANCE,
|
||||
@ -73,7 +76,7 @@ pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8]
|
||||
"stripe" => ICON_STRIPE,
|
||||
"tutanota" => ICON_TUTANOTA,
|
||||
_ => ICON_WEBAUTHN,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ICON_WEBAUTHN
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
//! (by running `make templates` in `core`)
|
||||
//! do not edit manually!
|
||||
|
||||
|
||||
use crate::strutil::TString;
|
||||
|
||||
<%
|
||||
icons: list[tuple[str, str]] = []
|
||||
for app in fido:
|
||||
@ -21,14 +24,14 @@ const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif"
|
||||
/// Translates icon name into its data.
|
||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||
/// supplied.
|
||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
||||
pub fn get_fido_icon_data(icon_name: Option<TString<'static>>) -> &'static [u8] {
|
||||
if let Some(icon_name) = icon_name {
|
||||
match icon_name.as_ref() {
|
||||
icon_name.map(|c| match c {
|
||||
% for icon_name, var_name in icons:
|
||||
"${icon_name}" => ICON_${var_name},
|
||||
% endfor
|
||||
_ => ICON_WEBAUTHN,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ICON_WEBAUTHN
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
use super::theme;
|
||||
use crate::ui::{
|
||||
component::{
|
||||
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
base::ComponentExt, label::Label, text::TextStyle, Child, Component, Event, EventCtx,
|
||||
},
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Offset, Rect},
|
||||
model_tt::component::{Button, ButtonMsg, CancelInfoConfirmMsg},
|
||||
},
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Offset, Rect},
|
||||
model_tt::component::{Button, ButtonMsg, CancelInfoConfirmMsg},
|
||||
};
|
||||
|
||||
pub struct Frame<T, U> {
|
||||
pub struct Frame<T> {
|
||||
border: Insets,
|
||||
title: Child<Label<U>>,
|
||||
subtitle: Option<Child<Label<U>>>,
|
||||
button: Option<Child<Button<&'static str>>>,
|
||||
title: Child<Label<'static>>,
|
||||
subtitle: Option<Child<Label<'static>>>,
|
||||
button: Option<Child<Button>>,
|
||||
button_msg: CancelInfoConfirmMsg,
|
||||
content: Child<T>,
|
||||
}
|
||||
@ -22,12 +25,16 @@ pub enum FrameMsg<T> {
|
||||
Button(CancelInfoConfirmMsg),
|
||||
}
|
||||
|
||||
impl<T, U> Frame<T, U>
|
||||
impl<T> Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
pub fn new(style: TextStyle, alignment: Alignment, title: U, content: T) -> Self {
|
||||
pub fn new(
|
||||
style: TextStyle,
|
||||
alignment: Alignment,
|
||||
title: TString<'static>,
|
||||
content: T,
|
||||
) -> Self {
|
||||
Self {
|
||||
title: Child::new(Label::new(title, alignment, style)),
|
||||
subtitle: None,
|
||||
@ -38,15 +45,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_aligned(style: TextStyle, title: U, content: T) -> Self {
|
||||
pub fn left_aligned(style: TextStyle, title: TString<'static>, content: T) -> Self {
|
||||
Self::new(style, Alignment::Start, title, content)
|
||||
}
|
||||
|
||||
pub fn right_aligned(style: TextStyle, title: U, content: T) -> Self {
|
||||
pub fn right_aligned(style: TextStyle, title: TString<'static>, content: T) -> Self {
|
||||
Self::new(style, Alignment::End, title, content)
|
||||
}
|
||||
|
||||
pub fn centered(style: TextStyle, title: U, content: T) -> Self {
|
||||
pub fn centered(style: TextStyle, title: TString<'static>, content: T) -> Self {
|
||||
Self::new(style, Alignment::Center, title, content)
|
||||
}
|
||||
|
||||
@ -55,7 +62,7 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_subtitle(mut self, style: TextStyle, subtitle: U) -> Self {
|
||||
pub fn with_subtitle(mut self, style: TextStyle, subtitle: TString<'static>) -> Self {
|
||||
self.subtitle = Some(Child::new(Label::new(
|
||||
subtitle,
|
||||
self.title.inner().alignment(),
|
||||
@ -91,7 +98,7 @@ where
|
||||
self.content.inner()
|
||||
}
|
||||
|
||||
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: U) {
|
||||
pub fn update_title(&mut self, ctx: &mut EventCtx, new_title: TString<'static>) {
|
||||
self.title.mutate(ctx, |ctx, t| {
|
||||
t.set_text(new_title);
|
||||
t.request_complete_repaint(ctx)
|
||||
@ -110,10 +117,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for Frame<T, U>
|
||||
impl<T> Component for Frame<T>
|
||||
where
|
||||
T: Component,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
type Msg = FrameMsg<T::Msg>;
|
||||
|
||||
@ -179,10 +185,9 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for Frame<T, U>
|
||||
impl<T> crate::trace::Trace for Frame<T>
|
||||
where
|
||||
T: crate::trace::Trace,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Frame");
|
||||
|
@ -269,14 +269,14 @@ impl crate::trace::Trace for Homescreen {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lockscreen {
|
||||
label: TString<'static>,
|
||||
pub struct Lockscreen<'a> {
|
||||
label: TString<'a>,
|
||||
bootscreen: bool,
|
||||
coinjoin_authorized: bool,
|
||||
}
|
||||
|
||||
impl Lockscreen {
|
||||
pub fn new(label: TString<'static>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
|
||||
impl<'a> Lockscreen<'a> {
|
||||
pub fn new(label: TString<'a>, bootscreen: bool, coinjoin_authorized: bool) -> Self {
|
||||
Lockscreen {
|
||||
label,
|
||||
bootscreen,
|
||||
@ -285,7 +285,7 @@ impl Lockscreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Lockscreen {
|
||||
impl Component for Lockscreen<'_> {
|
||||
type Msg = HomescreenMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -401,7 +401,7 @@ fn is_image_toif(buffer: &[u8]) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl crate::trace::Trace for Lockscreen {
|
||||
impl crate::trace::Trace for Lockscreen<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Lockscreen");
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ use heapless::String;
|
||||
const MAX_LENGTH: usize = 8;
|
||||
|
||||
pub struct Bip39Input {
|
||||
button: Button<&'static str>,
|
||||
button: Button,
|
||||
// used only to keep track of suggestion text color
|
||||
button_suggestion: Button<&'static str>,
|
||||
button_suggestion: Button,
|
||||
textbox: TextBox<MAX_LENGTH>,
|
||||
multi_tap: MultiTapKeyboard,
|
||||
options_num: Option<usize>,
|
||||
@ -263,7 +263,7 @@ impl Bip39Input {
|
||||
// Disabled button.
|
||||
self.button.disable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_pin());
|
||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
||||
self.button.set_content(ctx, ButtonContent::Text("".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use crate::ui::{
|
||||
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
||||
geometry::{Alignment2D, Grid, Offset, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg, Swipe, SwipeDirection},
|
||||
theme,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
||||
geometry::{Alignment2D, Grid, Offset, Rect},
|
||||
model_tt::{
|
||||
component::{Button, ButtonMsg, Swipe, SwipeDirection},
|
||||
theme,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -14,27 +17,26 @@ pub enum MnemonicKeyboardMsg {
|
||||
Previous,
|
||||
}
|
||||
|
||||
pub struct MnemonicKeyboard<T, U> {
|
||||
pub struct MnemonicKeyboard<T> {
|
||||
/// Initial prompt, displayed on empty input.
|
||||
prompt: Child<Maybe<Label<U>>>,
|
||||
prompt: Child<Maybe<Label<'static>>>,
|
||||
/// Backspace button.
|
||||
back: Child<Maybe<Button<&'static str>>>,
|
||||
back: Child<Maybe<Button>>,
|
||||
/// Input area, acting as the auto-complete and confirm button.
|
||||
input: Child<Maybe<T>>,
|
||||
/// Key buttons.
|
||||
keys: [Child<Button<&'static str>>; MNEMONIC_KEY_COUNT],
|
||||
keys: [Child<Button>; MNEMONIC_KEY_COUNT],
|
||||
/// Swipe controller - allowing for going to the previous word.
|
||||
swipe: Swipe,
|
||||
/// Whether going back is allowed (is not on the very first word).
|
||||
can_go_back: bool,
|
||||
}
|
||||
|
||||
impl<T, U> MnemonicKeyboard<T, U>
|
||||
impl<T> MnemonicKeyboard<T>
|
||||
where
|
||||
T: MnemonicInput,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
pub fn new(input: T, prompt: U, can_go_back: bool) -> Self {
|
||||
pub fn new(input: T, prompt: TString<'static>, can_go_back: bool) -> Self {
|
||||
// Input might be already pre-filled
|
||||
let prompt_visible = input.is_empty();
|
||||
|
||||
@ -57,7 +59,7 @@ where
|
||||
)),
|
||||
input: Child::new(Maybe::new(theme::BG, input, !prompt_visible)),
|
||||
keys: T::keys()
|
||||
.map(|t| Button::with_text(t).styled(theme::button_pin()))
|
||||
.map(|t| Button::with_text(t.into()).styled(theme::button_pin()))
|
||||
.map(Child::new),
|
||||
swipe: Swipe::new().right(),
|
||||
can_go_back,
|
||||
@ -99,10 +101,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Component for MnemonicKeyboard<T, U>
|
||||
impl<T> Component for MnemonicKeyboard<T>
|
||||
where
|
||||
T: MnemonicInput,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
type Msg = MnemonicKeyboardMsg;
|
||||
|
||||
@ -210,10 +211,9 @@ pub enum MnemonicInputMsg {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for MnemonicKeyboard<T, U>
|
||||
impl<T> crate::trace::Trace for MnemonicKeyboard<T>
|
||||
where
|
||||
T: MnemonicInput + crate::trace::Trace,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("MnemonicKeyboard");
|
||||
|
@ -1,16 +1,19 @@
|
||||
use crate::ui::{
|
||||
component::{
|
||||
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
base::ComponentExt, text::common::TextBox, Child, Component, Event, EventCtx, Never,
|
||||
},
|
||||
display,
|
||||
geometry::{Grid, Offset, Rect},
|
||||
model_tt::component::{
|
||||
button::{Button, ButtonContent, ButtonMsg},
|
||||
keyboard::common::{paint_pending_marker, MultiTapKeyboard},
|
||||
swipe::{Swipe, SwipeDirection},
|
||||
theme, ScrollBar,
|
||||
},
|
||||
util::long_line_content_with_ellipsis,
|
||||
},
|
||||
display,
|
||||
geometry::{Grid, Offset, Rect},
|
||||
model_tt::component::{
|
||||
button::{Button, ButtonContent, ButtonMsg},
|
||||
keyboard::common::{paint_pending_marker, MultiTapKeyboard},
|
||||
swipe::{Swipe, SwipeDirection},
|
||||
theme, ScrollBar,
|
||||
},
|
||||
util::long_line_content_with_ellipsis,
|
||||
};
|
||||
|
||||
use core::cell::Cell;
|
||||
@ -23,9 +26,9 @@ pub enum PassphraseKeyboardMsg {
|
||||
pub struct PassphraseKeyboard {
|
||||
page_swipe: Swipe,
|
||||
input: Child<Input>,
|
||||
back: Child<Button<&'static str>>,
|
||||
confirm: Child<Button<&'static str>>,
|
||||
keys: [Child<Button<&'static str>>; KEY_COUNT],
|
||||
back: Child<Button>,
|
||||
confirm: Child<Button>,
|
||||
keys: [Child<Button>; KEY_COUNT],
|
||||
scrollbar: ScrollBar,
|
||||
fade: Cell<bool>,
|
||||
}
|
||||
@ -69,20 +72,20 @@ impl PassphraseKeyboard {
|
||||
}
|
||||
}
|
||||
|
||||
fn key_text(content: &ButtonContent<&'static str>) -> &'static str {
|
||||
fn key_text(content: &ButtonContent) -> TString<'static> {
|
||||
match content {
|
||||
ButtonContent::Text(text) => text,
|
||||
ButtonContent::Icon(_) => " ",
|
||||
ButtonContent::IconAndText(_) => " ",
|
||||
ButtonContent::Empty => "",
|
||||
ButtonContent::IconBlend(_, _, _) => "",
|
||||
ButtonContent::Text(text) => *text,
|
||||
ButtonContent::Icon(_) => " ".into(),
|
||||
ButtonContent::IconAndText(_) => " ".into(),
|
||||
ButtonContent::Empty => "".into(),
|
||||
ButtonContent::IconBlend(_, _, _) => "".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn key_content(text: &'static str) -> ButtonContent<&'static str> {
|
||||
fn key_content(text: &'static str) -> ButtonContent {
|
||||
match text {
|
||||
" " => ButtonContent::Icon(theme::ICON_SPACE),
|
||||
t => ButtonContent::Text(t),
|
||||
t => ButtonContent::Text(t.into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +275,7 @@ impl Component for PassphraseKeyboard {
|
||||
// character in textbox. If not, let's just append the first character.
|
||||
let text = Self::key_text(btn.inner().content());
|
||||
self.input.mutate(ctx, |ctx, i| {
|
||||
let edit = i.multi_tap.click_key(ctx, key, text);
|
||||
let edit = text.map(|c| i.multi_tap.click_key(ctx, key, c));
|
||||
i.textbox.apply(ctx, edit);
|
||||
});
|
||||
self.after_edit(ctx);
|
||||
|
@ -2,6 +2,7 @@ use core::mem;
|
||||
use heapless::String;
|
||||
|
||||
use crate::{
|
||||
strutil::TString,
|
||||
time::Duration,
|
||||
trezorhal::random,
|
||||
ui::{
|
||||
@ -39,32 +40,29 @@ const HEADER_PADDING: Insets = Insets::new(
|
||||
HEADER_PADDING_SIDE,
|
||||
);
|
||||
|
||||
pub struct PinKeyboard<T> {
|
||||
pub struct PinKeyboard<'a> {
|
||||
allow_cancel: bool,
|
||||
major_prompt: Child<Label<T>>,
|
||||
minor_prompt: Child<Label<T>>,
|
||||
major_warning: Option<Child<Label<T>>>,
|
||||
major_prompt: Child<Label<'a>>,
|
||||
minor_prompt: Child<Label<'a>>,
|
||||
major_warning: Option<Child<Label<'a>>>,
|
||||
textbox: Child<PinDots>,
|
||||
textbox_pad: Pad,
|
||||
erase_btn: Child<Maybe<Button<&'static str>>>,
|
||||
cancel_btn: Child<Maybe<Button<&'static str>>>,
|
||||
confirm_btn: Child<Button<&'static str>>,
|
||||
digit_btns: [Child<Button<&'static str>>; DIGIT_COUNT],
|
||||
erase_btn: Child<Maybe<Button>>,
|
||||
cancel_btn: Child<Maybe<Button>>,
|
||||
confirm_btn: Child<Button>,
|
||||
digit_btns: [Child<Button>; DIGIT_COUNT],
|
||||
warning_timer: Option<TimerToken>,
|
||||
}
|
||||
|
||||
impl<T> PinKeyboard<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl<'a> PinKeyboard<'a> {
|
||||
// Label position fine-tuning.
|
||||
const MAJOR_OFF: Offset = Offset::y(11);
|
||||
const MINOR_OFF: Offset = Offset::y(11);
|
||||
|
||||
pub fn new(
|
||||
major_prompt: T,
|
||||
minor_prompt: T,
|
||||
major_warning: Option<T>,
|
||||
major_prompt: TString<'a>,
|
||||
minor_prompt: TString<'a>,
|
||||
major_warning: Option<TString<'a>>,
|
||||
allow_cancel: bool,
|
||||
) -> Self {
|
||||
// Control buttons.
|
||||
@ -102,12 +100,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_digit_buttons() -> [Child<Button<&'static str>>; DIGIT_COUNT] {
|
||||
fn generate_digit_buttons() -> [Child<Button>; DIGIT_COUNT] {
|
||||
// Generate a random sequence of digits from 0 to 9.
|
||||
let mut digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
|
||||
random::shuffle(&mut digits);
|
||||
digits
|
||||
.map(Button::with_text)
|
||||
.map(|c| Button::with_text(c.into()))
|
||||
.map(|b| b.styled(theme::button_pin()))
|
||||
.map(Child::new)
|
||||
}
|
||||
@ -146,10 +144,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for PinKeyboard<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl Component for PinKeyboard<'_> {
|
||||
type Msg = PinKeyboardMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -238,7 +233,9 @@ where
|
||||
for btn in &mut self.digit_btns {
|
||||
if let Some(Clicked) = btn.event(ctx, event) {
|
||||
if let ButtonContent::Text(text) = btn.inner().content() {
|
||||
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text));
|
||||
text.map(|text| {
|
||||
self.textbox.mutate(ctx, |ctx, t| t.push(ctx, text));
|
||||
});
|
||||
self.pin_modified(ctx);
|
||||
return None;
|
||||
}
|
||||
@ -460,18 +457,18 @@ impl Component for PinDots {
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for PinKeyboard<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl crate::trace::Trace for PinKeyboard<'_> {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("PinKeyboard");
|
||||
// So that debuglink knows the locations of the buttons
|
||||
let mut digits_order: String<10> = String::new();
|
||||
for btn in self.digit_btns.iter() {
|
||||
let btn_content = btn.inner().content();
|
||||
|
||||
if let ButtonContent::Text(text) = btn_content {
|
||||
unwrap!(digits_order.push_str(text));
|
||||
text.map(|text| {
|
||||
unwrap!(digits_order.push_str(text));
|
||||
});
|
||||
}
|
||||
}
|
||||
t.string("digits_order", digits_order.as_str().into());
|
||||
|
@ -28,7 +28,7 @@ use crate::{
|
||||
const MAX_LENGTH: usize = 8;
|
||||
|
||||
pub struct Slip39Input {
|
||||
button: Button<&'static str>,
|
||||
button: Button,
|
||||
textbox: TextBox<MAX_LENGTH>,
|
||||
multi_tap: MultiTapKeyboard,
|
||||
final_word: Option<&'static str>,
|
||||
@ -293,7 +293,7 @@ impl Slip39Input {
|
||||
} else {
|
||||
// Disabled button.
|
||||
self.button.disable(ctx);
|
||||
self.button.set_content(ctx, ButtonContent::Text(""));
|
||||
self.button.set_content(ctx, ButtonContent::Text("".into()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ const LABELS: [&str; 5] = ["12", "18", "20", "24", "33"];
|
||||
const CELLS: [(usize, usize); 5] = [(0, 0), (0, 2), (0, 4), (1, 0), (1, 2)];
|
||||
|
||||
pub struct SelectWordCount {
|
||||
button: [Button<&'static str>; NUMBERS.len()],
|
||||
button: [Button; NUMBERS.len()],
|
||||
}
|
||||
|
||||
pub enum SelectWordCountMsg {
|
||||
@ -22,7 +22,7 @@ pub enum SelectWordCountMsg {
|
||||
impl SelectWordCount {
|
||||
pub fn new() -> Self {
|
||||
SelectWordCount {
|
||||
button: LABELS.map(|t| Button::with_text(t).styled(theme::button_pin())),
|
||||
button: LABELS.map(|t| Button::with_text(t.into()).styled(theme::button_pin())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::buffer::StrBuffer,
|
||||
strutil::{self, StringType},
|
||||
translations::TR,
|
||||
ui::{
|
||||
@ -31,8 +30,8 @@ where
|
||||
input: Child<NumberInput>,
|
||||
paragraphs: Child<Paragraphs<Paragraph<'static>>>,
|
||||
paragraphs_pad: Pad,
|
||||
info_button: Child<Button<StrBuffer>>,
|
||||
confirm_button: Child<Button<StrBuffer>>,
|
||||
info_button: Child<Button>,
|
||||
confirm_button: Child<Button>,
|
||||
}
|
||||
|
||||
impl<T, F> NumberInputDialog<T, F>
|
||||
@ -48,8 +47,8 @@ where
|
||||
input: NumberInput::new(min, max, init_value).into_child(),
|
||||
paragraphs: Paragraphs::new(Paragraph::new(&theme::TEXT_NORMAL, text)).into_child(),
|
||||
paragraphs_pad: Pad::with_background(theme::BG),
|
||||
info_button: Button::with_text(TR::buttons__info.try_into()?).into_child(),
|
||||
confirm_button: Button::with_text(TR::buttons__continue.try_into()?)
|
||||
info_button: Button::with_text(TR::buttons__info.into()).into_child(),
|
||||
confirm_button: Button::with_text(TR::buttons__continue.into())
|
||||
.styled(theme::button_confirm())
|
||||
.into_child(),
|
||||
})
|
||||
@ -154,8 +153,8 @@ pub enum NumberInputMsg {
|
||||
|
||||
pub struct NumberInput {
|
||||
area: Rect,
|
||||
dec: Child<Button<&'static str>>,
|
||||
inc: Child<Button<&'static str>>,
|
||||
dec: Child<Button>,
|
||||
inc: Child<Button>,
|
||||
min: u32,
|
||||
max: u32,
|
||||
value: u32,
|
||||
@ -163,10 +162,10 @@ pub struct NumberInput {
|
||||
|
||||
impl NumberInput {
|
||||
pub fn new(min: u32, max: u32, value: u32) -> Self {
|
||||
let dec = Button::with_text("-")
|
||||
let dec = Button::with_text("-".into())
|
||||
.styled(theme::button_counter())
|
||||
.into_child();
|
||||
let inc = Button::with_text("+")
|
||||
let inc = Button::with_text("+".into())
|
||||
.styled(theme::button_counter())
|
||||
.into_child();
|
||||
let value = value.clamp(min, max);
|
||||
@ -217,7 +216,7 @@ impl Component for NumberInput {
|
||||
let mut buf = [0u8; 10];
|
||||
if let Some(text) = strutil::format_i64(self.value as i64, &mut buf) {
|
||||
let digit_font = Font::DEMIBOLD;
|
||||
let y_offset = digit_font.text_height() / 2 + Button::<&str>::BASELINE_OFFSET;
|
||||
let y_offset = digit_font.text_height() / 2 + Button::BASELINE_OFFSET;
|
||||
display::rect_fill(self.area, theme::BG);
|
||||
display::text_center(
|
||||
self.area.center() + Offset::y(y_offset),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::buffer::StrBuffer,
|
||||
strutil::TString,
|
||||
time::Instant,
|
||||
translations::TR,
|
||||
ui::{
|
||||
@ -19,7 +19,7 @@ use super::{
|
||||
|
||||
/// Allows pagination of inner component. Shows scroll bar, confirm & cancel
|
||||
/// buttons. Optionally handles hold-to-confirm with loader.
|
||||
pub struct ButtonPage<T, U> {
|
||||
pub struct ButtonPage<T> {
|
||||
/// Inner component.
|
||||
content: T,
|
||||
/// Cleared when page changes.
|
||||
@ -29,10 +29,10 @@ pub struct ButtonPage<T, U> {
|
||||
scrollbar: ScrollBar,
|
||||
/// Hold-to-confirm mode whenever this is `Some(loader)`.
|
||||
loader: Option<Loader>,
|
||||
button_cancel: Option<Button<U>>,
|
||||
button_confirm: Button<U>,
|
||||
button_prev: Button<&'static str>,
|
||||
button_next: Button<&'static str>,
|
||||
button_cancel: Option<Button>,
|
||||
button_confirm: Button,
|
||||
button_prev: Button,
|
||||
button_next: Button,
|
||||
/// Show cancel button instead of back button.
|
||||
cancel_from_any_page: bool,
|
||||
/// Whether to pass-through left swipe to parent component.
|
||||
@ -43,24 +43,23 @@ pub struct ButtonPage<T, U> {
|
||||
fade: Option<u16>,
|
||||
}
|
||||
|
||||
impl<T> ButtonPage<T, StrBuffer>
|
||||
impl<T> ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
{
|
||||
pub fn with_hold(mut self) -> Result<Self, Error> {
|
||||
self.button_confirm = Button::with_text(TR::buttons__hold_to_confirm.try_into()?)
|
||||
.styled(theme::button_confirm());
|
||||
self.button_confirm =
|
||||
Button::with_text(TR::buttons__hold_to_confirm.into()).styled(theme::button_confirm());
|
||||
self.loader = Some(Loader::new());
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ButtonPage<T, U>
|
||||
impl<T> ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
U: AsRef<str> + From<&'static str>,
|
||||
{
|
||||
pub fn new(content: T, background: Color) -> Self {
|
||||
Self {
|
||||
@ -85,13 +84,17 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cancel_confirm(mut self, left: Option<U>, right: Option<U>) -> Self {
|
||||
pub fn with_cancel_confirm(
|
||||
mut self,
|
||||
left: Option<TString<'static>>,
|
||||
right: Option<TString<'static>>,
|
||||
) -> Self {
|
||||
let cancel = match left {
|
||||
Some(verb) => match verb.as_ref() {
|
||||
Some(verb) => verb.map(|s| match s {
|
||||
"^" => Button::with_icon(theme::ICON_UP),
|
||||
"<" => Button::with_icon(theme::ICON_BACK),
|
||||
_ => Button::with_text(verb),
|
||||
},
|
||||
}),
|
||||
_ => Button::with_icon(theme::ICON_CANCEL),
|
||||
};
|
||||
let confirm = match right {
|
||||
@ -282,11 +285,10 @@ enum HandleResult<T> {
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl<T, U> Component for ButtonPage<T, U>
|
||||
impl<T> Component for ButtonPage<T>
|
||||
where
|
||||
T: Paginate,
|
||||
T: Component,
|
||||
U: AsRef<str> + From<&'static str>,
|
||||
{
|
||||
type Msg = PageMsg<T::Msg>;
|
||||
|
||||
@ -294,7 +296,7 @@ where
|
||||
let small_left_button = match (&self.button_cancel, &self.button_confirm) {
|
||||
(None, _) => true,
|
||||
(Some(cancel), confirm) => match (cancel.content(), confirm.content()) {
|
||||
(ButtonContent::Text(t), _) => t.as_ref().len() <= 4,
|
||||
(ButtonContent::Text(t), _) => t.len() <= 4,
|
||||
(ButtonContent::Icon(_), ButtonContent::Icon(_)) => false,
|
||||
_ => true,
|
||||
},
|
||||
@ -425,7 +427,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T, U> crate::trace::Trace for ButtonPage<T, U>
|
||||
impl<T> crate::trace::Trace for ButtonPage<T>
|
||||
where
|
||||
T: crate::trace::Trace,
|
||||
{
|
||||
@ -528,10 +530,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn paragraphs_empty() {
|
||||
let mut page = ButtonPage::<_, &'static str>::new(
|
||||
Paragraphs::<[Paragraph<'static>; 0]>::new([]),
|
||||
theme::BG,
|
||||
);
|
||||
let mut page = ButtonPage::new(Paragraphs::<[Paragraph<'static>; 0]>::new([]), theme::BG);
|
||||
page.place(SCREEN);
|
||||
|
||||
let expected = serde_json::json!({
|
||||
@ -554,7 +553,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn paragraphs_single() {
|
||||
let mut page = ButtonPage::<_, &'static str>::new(
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(
|
||||
&theme::TEXT_NORMAL,
|
||||
@ -592,7 +591,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn paragraphs_one_long() {
|
||||
let mut page = ButtonPage::<_, &'static str>::new(
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new(
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
@ -650,7 +649,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn paragraphs_three_long() {
|
||||
let mut page = ButtonPage::<_, &'static str>::new(
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(
|
||||
&theme::TEXT_BOLD,
|
||||
@ -750,7 +749,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn paragraphs_hard_break() {
|
||||
let mut page = ButtonPage::<_, &'static str>::new(
|
||||
let mut page = ButtonPage::new(
|
||||
Paragraphs::new([
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Short one.").break_after(),
|
||||
Paragraph::new(&theme::TEXT_NORMAL, "Short two.").break_after(),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::mem;
|
||||
|
||||
use crate::{
|
||||
strutil::{StringType, TString},
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{
|
||||
base::ComponentExt,
|
||||
@ -18,8 +18,8 @@ use crate::{
|
||||
|
||||
use super::theme;
|
||||
|
||||
pub struct Progress<T> {
|
||||
title: Child<Label<T>>,
|
||||
pub struct Progress {
|
||||
title: Child<Label<'static>>,
|
||||
value: u16,
|
||||
loader_y_offset: i16,
|
||||
indeterminate: bool,
|
||||
@ -27,13 +27,14 @@ pub struct Progress<T> {
|
||||
description_pad: Pad,
|
||||
}
|
||||
|
||||
impl<T> Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl Progress {
|
||||
const AREA: Rect = constant::screen().inset(theme::borders());
|
||||
|
||||
pub fn new(title: T, indeterminate: bool, description: TString<'static>) -> Self {
|
||||
pub fn new(
|
||||
title: TString<'static>,
|
||||
indeterminate: bool,
|
||||
description: TString<'static>,
|
||||
) -> Self {
|
||||
Self {
|
||||
title: Label::centered(title, theme::label_progress()).into_child(),
|
||||
value: 0,
|
||||
@ -48,10 +49,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Component for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl Component for Progress {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||
@ -117,10 +115,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "ui_debug")]
|
||||
impl<T> crate::trace::Trace for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl crate::trace::Trace for Progress {
|
||||
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||
t.component("Progress");
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
strutil::StringType,
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad},
|
||||
constant::screen,
|
||||
@ -41,21 +41,23 @@ impl ResultStyle {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResultFooter<'a, T> {
|
||||
pub struct ResultFooter<'a> {
|
||||
style: &'a ResultStyle,
|
||||
text: Label<T>,
|
||||
text: Label<'a>,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<str>> ResultFooter<'a, T> {
|
||||
pub fn new(text: Label<T>, style: &'a ResultStyle) -> Self {
|
||||
impl<'a> ResultFooter<'a> {
|
||||
pub fn new(text: Label<'a>, style: &'a ResultStyle) -> Self {
|
||||
Self {
|
||||
style,
|
||||
text,
|
||||
area: Rect::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResultFooter<'_> {
|
||||
pub const fn split_bounds() -> (Rect, Rect) {
|
||||
let main_area = Rect::new(
|
||||
Point::new(RESULT_PADDING, 0),
|
||||
@ -69,7 +71,7 @@ impl<'a, T: AsRef<str>> ResultFooter<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
|
||||
impl Component for ResultFooter<'_> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
@ -78,6 +80,10 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
|
||||
bounds
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
// divider line
|
||||
let bar = Rect::from_center_and_size(
|
||||
@ -89,27 +95,23 @@ impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
|
||||
// footer text
|
||||
self.text.paint();
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResultScreen<'a, T> {
|
||||
pub struct ResultScreen<'a> {
|
||||
bg: Pad,
|
||||
footer_pad: Pad,
|
||||
style: &'a ResultStyle,
|
||||
icon: Icon,
|
||||
message: Child<Label<T>>,
|
||||
footer: Child<ResultFooter<'a, &'a str>>,
|
||||
message: Child<Label<'a>>,
|
||||
footer: Child<ResultFooter<'a>>,
|
||||
}
|
||||
|
||||
impl<'a, T: StringType> ResultScreen<'a, T> {
|
||||
impl<'a> ResultScreen<'a> {
|
||||
pub fn new(
|
||||
style: &'a ResultStyle,
|
||||
icon: Icon,
|
||||
message: T,
|
||||
footer: Label<&'a str>,
|
||||
message: TString<'a>,
|
||||
footer: Label<'a>,
|
||||
complete_draw: bool,
|
||||
) -> Self {
|
||||
let mut instance = Self {
|
||||
@ -130,13 +132,13 @@ impl<'a, T: StringType> ResultScreen<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: StringType> Component for ResultScreen<'a, T> {
|
||||
impl<'a> Component for ResultScreen<'a> {
|
||||
type Msg = Never;
|
||||
|
||||
fn place(&mut self, _bounds: Rect) -> Rect {
|
||||
self.bg.place(screen());
|
||||
|
||||
let (main_area, footer_area) = ResultFooter::<&'a str>::split_bounds();
|
||||
let (main_area, footer_area) = ResultFooter::split_bounds();
|
||||
|
||||
self.footer_pad.place(footer_area);
|
||||
self.footer.place(footer_area);
|
||||
|
@ -100,10 +100,9 @@ impl TryFrom<SelectWordCountMsg> for Obj {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, U> ComponentMsgObj for FidoConfirm<F, T, U>
|
||||
impl<F, U> ComponentMsgObj for FidoConfirm<F, U>
|
||||
where
|
||||
F: Fn(usize) -> T,
|
||||
T: StringType,
|
||||
F: Fn(usize) -> TString<'static>,
|
||||
U: Component<Msg = CancelConfirmMsg>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
@ -141,10 +140,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for PinKeyboard<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl ComponentMsgObj for PinKeyboard<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
PinKeyboardMsg::Confirmed => self.pin().try_into(),
|
||||
@ -162,10 +158,9 @@ impl ComponentMsgObj for PassphraseKeyboard {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for MnemonicKeyboard<T, U>
|
||||
impl<T> ComponentMsgObj for MnemonicKeyboard<T>
|
||||
where
|
||||
T: MnemonicInput,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -181,10 +176,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for Frame<T, U>
|
||||
impl<T> ComponentMsgObj for Frame<T>
|
||||
where
|
||||
T: ComponentMsgObj,
|
||||
U: AsRef<str>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -194,10 +188,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for ButtonPage<T, U>
|
||||
impl<T> ComponentMsgObj for ButtonPage<T>
|
||||
where
|
||||
T: Component + Paginate,
|
||||
U: AsRef<str> + From<&'static str>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
@ -269,10 +262,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for Progress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
impl ComponentMsgObj for Progress {
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
unreachable!()
|
||||
}
|
||||
@ -286,7 +276,7 @@ impl ComponentMsgObj for Homescreen {
|
||||
}
|
||||
}
|
||||
|
||||
impl ComponentMsgObj for Lockscreen {
|
||||
impl ComponentMsgObj for Lockscreen<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
HomescreenMsg::Dismissed => Ok(CANCELLED.as_obj()),
|
||||
@ -342,9 +332,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> ComponentMsgObj for CoinJoinProgress<T, U>
|
||||
impl<U> ComponentMsgObj for CoinJoinProgress<U>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
U: Component<Msg = Never>,
|
||||
{
|
||||
fn msg_try_into_obj(&self, _msg: Self::Msg) -> Result<Obj, Error> {
|
||||
@ -352,10 +341,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentMsgObj for super::component::bl_confirm::Confirm<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
impl ComponentMsgObj for super::component::bl_confirm::Confirm<'_> {
|
||||
fn msg_try_into_obj(&self, msg: Self::Msg) -> Result<Obj, Error> {
|
||||
match msg {
|
||||
super::component::bl_confirm::ConfirmMsg::Cancel => Ok(CANCELLED.as_obj()),
|
||||
@ -401,12 +387,17 @@ extern "C" fn new_confirm_action(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let mut page = if hold {
|
||||
ButtonPage::new(paragraphs, theme::BG).with_hold()?
|
||||
} else {
|
||||
ButtonPage::new(paragraphs, theme::BG).with_cancel_confirm(verb_cancel, verb)
|
||||
ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_cancel_confirm(verb_cancel.map(|c| c.into()), verb.map(|c| c.into()))
|
||||
};
|
||||
if hold && hold_danger {
|
||||
page = page.with_confirm_style(theme::button_danger())
|
||||
}
|
||||
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?;
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title.into(),
|
||||
page,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -438,9 +429,9 @@ extern "C" fn new_confirm_emphasized(n_args: usize, args: *const Obj, kwargs: *m
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
ButtonPage::new(FormattedText::new(ops).vertically_centered(), theme::BG)
|
||||
.with_cancel_confirm(None, verb),
|
||||
.with_cancel_confirm(None, verb.map(|v| v.into())),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -530,14 +521,14 @@ impl ConfirmBlobParams {
|
||||
|
||||
let mut page = ButtonPage::new(paragraphs, theme::BG);
|
||||
if let Some(verb) = self.verb {
|
||||
page = page.with_cancel_confirm(self.verb_cancel, Some(verb))
|
||||
page = page.with_cancel_confirm(self.verb_cancel.map(|v| v.into()), Some(verb.into()))
|
||||
}
|
||||
if self.hold {
|
||||
page = page.with_hold()?
|
||||
}
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), self.title, page);
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), self.title.into(), page);
|
||||
if let Some(subtitle) = self.subtitle {
|
||||
frame = frame.with_subtitle(theme::label_subtitle(), subtitle);
|
||||
frame = frame.with_subtitle(theme::label_subtitle(), subtitle.into());
|
||||
}
|
||||
if self.info_button {
|
||||
frame = frame.with_info_button();
|
||||
@ -604,10 +595,10 @@ extern "C" fn new_confirm_address(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_swipe_left()
|
||||
.with_cancel_confirm(None, Some(verb)),
|
||||
.with_cancel_confirm(None, Some(verb.into())),
|
||||
)
|
||||
.with_info_button(),
|
||||
)?;
|
||||
@ -628,13 +619,17 @@ extern "C" fn new_confirm_properties(n_args: usize, args: *const Obj, kwargs: *m
|
||||
&theme::TEXT_MONO,
|
||||
&theme::TEXT_MONO,
|
||||
)?;
|
||||
let page: ButtonPage<_, StrBuffer> = if hold {
|
||||
let page = if hold {
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?
|
||||
} else {
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(TR::buttons__confirm.try_into()?))
|
||||
.with_cancel_confirm(None, Some(TR::buttons__confirm.into()))
|
||||
};
|
||||
let obj = LayoutObj::new(Frame::left_aligned(theme::label_title(), title, page))?;
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title.into(),
|
||||
page,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -665,11 +660,10 @@ extern "C" fn new_confirm_homescreen(n_args: usize, args: *const Obj, kwargs: *m
|
||||
_ => return Err(value_error!("Invalid image.")),
|
||||
};
|
||||
|
||||
let tr_change: StrBuffer = TR::buttons__change.try_into()?;
|
||||
let buttons = Button::cancel_confirm_text(None, Some(tr_change));
|
||||
let buttons = Button::cancel_confirm_text(None, Some(TR::buttons__change.into()));
|
||||
let obj = LayoutObj::new(Frame::centered(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(painter::jpeg_painter(buffer_func, size, 1), buttons),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -691,12 +685,12 @@ extern "C" fn new_confirm_reset_device(n_args: usize, args: *const Obj, kwargs:
|
||||
let paragraphs = Paragraphs::new(par_array);
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_text(button).styled(theme::button_confirm()),
|
||||
Button::with_text(button.into()).styled(theme::button_confirm()),
|
||||
true,
|
||||
);
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(paragraphs, buttons),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -768,7 +762,7 @@ extern "C" fn new_show_info_with_cancel(n_args: usize, args: *const Obj, kwargs:
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
SimplePage::new(paragraphs.into_paragraphs(), axis, theme::BG)
|
||||
.with_swipe_right_to_go_back(),
|
||||
)
|
||||
@ -824,15 +818,14 @@ extern "C" fn new_confirm_total(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_NORMAL, label).no_break());
|
||||
paragraphs.add(Paragraph::new(&theme::TEXT_MONO, value));
|
||||
}
|
||||
let mut page: ButtonPage<_, StrBuffer> =
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
|
||||
let mut page = ButtonPage::new(paragraphs.into_paragraphs(), theme::BG).with_hold()?;
|
||||
if cancel_arrow {
|
||||
page = page.with_cancel_arrow()
|
||||
}
|
||||
if info_button {
|
||||
page = page.with_swipe_left();
|
||||
}
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), title, page);
|
||||
let mut frame = Frame::left_aligned(theme::label_title(), title.into(), page);
|
||||
if info_button {
|
||||
frame = frame.with_info_button();
|
||||
}
|
||||
@ -861,12 +854,11 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
||||
Paragraph::new(&theme::TEXT_MONO, amount_new),
|
||||
]);
|
||||
|
||||
let tr_title: StrBuffer = TR::modify_amount__title.try_into()?;
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
tr_title,
|
||||
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG)
|
||||
.with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.try_into()?)),
|
||||
TR::modify_amount__title.into(),
|
||||
ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_cancel_confirm(Some("^".into()), Some(TR::buttons__continue.into())),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -908,8 +900,8 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
||||
let obj = LayoutObj::new(
|
||||
Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG)
|
||||
title.into(),
|
||||
ButtonPage::new(paragraphs, theme::BG)
|
||||
.with_hold()?
|
||||
.with_swipe_left(),
|
||||
)
|
||||
@ -963,7 +955,7 @@ fn new_show_modal(
|
||||
title,
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_text(button).styled(button_style),
|
||||
Button::with_text(button.into()).styled(button_style),
|
||||
false,
|
||||
),
|
||||
)
|
||||
@ -977,9 +969,9 @@ fn new_show_modal(
|
||||
IconDialog::new(
|
||||
icon,
|
||||
title,
|
||||
theme::button_bar(Button::with_text(button).styled(button_style).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
theme::button_bar(Button::with_text(button.into()).styled(button_style).map(
|
||||
|msg| (matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed),
|
||||
)),
|
||||
)
|
||||
.with_value(value)
|
||||
.with_description(description),
|
||||
@ -1008,7 +1000,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let app_name: StrBuffer = kwargs.get(Qstr::MP_QSTR_app_name)?.try_into()?;
|
||||
let icon: Option<StrBuffer> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
|
||||
let icon: Option<TString> = kwargs.get(Qstr::MP_QSTR_icon_name)?.try_into_option()?;
|
||||
let accounts: Gc<List> = kwargs.get(Qstr::MP_QSTR_accounts)?.try_into()?;
|
||||
|
||||
// Cache the page count so that we can move `accounts` into the closure.
|
||||
@ -1023,14 +1015,17 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
|
||||
let controls = Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::<StrBuffer>::with_text(TR::buttons__confirm.try_into()?)
|
||||
.styled(theme::button_confirm()),
|
||||
Button::with_text(TR::buttons__confirm.into()).styled(theme::button_confirm()),
|
||||
true,
|
||||
);
|
||||
|
||||
let fido_page = FidoConfirm::new(app_name, get_page, page_count, icon, controls);
|
||||
let fido_page = FidoConfirm::new(app_name.into(), get_page, page_count, icon, controls);
|
||||
|
||||
let obj = LayoutObj::new(Frame::centered(theme::label_title(), title, fido_page))?;
|
||||
let obj = LayoutObj::new(Frame::centered(
|
||||
theme::label_title(),
|
||||
title.into(),
|
||||
fido_page,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1098,7 +1093,7 @@ extern "C" fn new_show_mismatch(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
title,
|
||||
Button::cancel_confirm(
|
||||
Button::with_icon(theme::ICON_BACK),
|
||||
Button::with_text(button).styled(theme::button_reset()),
|
||||
Button::with_text(button.into()).styled(theme::button_reset()),
|
||||
true,
|
||||
),
|
||||
)
|
||||
@ -1128,10 +1123,10 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
let obj = if let Some(t) = title {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
t,
|
||||
t.into(),
|
||||
Dialog::new(
|
||||
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
|
||||
theme::button_bar(Button::with_text(button).map(|msg| {
|
||||
theme::button_bar(Button::with_text(button.into()).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
),
|
||||
@ -1142,7 +1137,7 @@ extern "C" fn new_show_simple(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
theme::borders(),
|
||||
Dialog::new(
|
||||
Paragraphs::new([Paragraph::new(&theme::TEXT_NORMAL, description)]),
|
||||
theme::button_bar(Button::with_text(button).map(|msg| {
|
||||
theme::button_bar(Button::with_text(button.into()).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
),
|
||||
@ -1185,11 +1180,11 @@ extern "C" fn new_confirm_with_info(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
}
|
||||
}
|
||||
|
||||
let buttons = Button::cancel_info_confirm(button, info_button);
|
||||
let buttons = Button::cancel_info_confirm(button.into(), info_button.into());
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(paragraphs.into_paragraphs(), buttons),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1214,9 +1209,9 @@ extern "C" fn new_confirm_more(n_args: usize, args: *const Obj, kwargs: *mut Map
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(button))
|
||||
.with_cancel_confirm(None, Some(button.into()))
|
||||
.with_confirm_style(theme::button_default())
|
||||
.with_back_button(),
|
||||
))?;
|
||||
@ -1237,11 +1232,10 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
||||
]);
|
||||
|
||||
let tr_title: StrBuffer = TR::coinjoin__title.try_into()?;
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
tr_title,
|
||||
ButtonPage::<_, StrBuffer>::new(paragraphs, theme::BG).with_hold()?,
|
||||
TR::coinjoin__title.into(),
|
||||
ButtonPage::new(paragraphs, theme::BG).with_hold()?,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1255,11 +1249,16 @@ extern "C" fn new_request_pin(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
let allow_cancel: bool = kwargs.get_or(Qstr::MP_QSTR_allow_cancel, true)?;
|
||||
let warning: bool = kwargs.get_or(Qstr::MP_QSTR_wrong_pin, false)?;
|
||||
let warning = if warning {
|
||||
Some(TR::pin__wrong_pin.try_into()?)
|
||||
Some(TR::pin__wrong_pin.into())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let obj = LayoutObj::new(PinKeyboard::new(prompt, subprompt, warning, allow_cancel))?;
|
||||
let obj = LayoutObj::new(PinKeyboard::new(
|
||||
prompt.into(),
|
||||
subprompt.into(),
|
||||
warning,
|
||||
allow_cancel,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1282,7 +1281,7 @@ extern "C" fn new_request_bip39(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?;
|
||||
let obj = LayoutObj::new(MnemonicKeyboard::new(
|
||||
Bip39Input::prefilled_word(prefill_word.as_ref()),
|
||||
prompt,
|
||||
prompt.into(),
|
||||
can_go_back,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1297,7 +1296,7 @@ extern "C" fn new_request_slip39(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
let can_go_back: bool = kwargs.get(Qstr::MP_QSTR_can_go_back)?.try_into()?;
|
||||
let obj = LayoutObj::new(MnemonicKeyboard::new(
|
||||
Slip39Input::prefilled_word(prefill_word.as_ref()),
|
||||
prompt,
|
||||
prompt.into(),
|
||||
can_go_back,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1310,12 +1309,12 @@ extern "C" fn new_select_word(n_args: usize, args: *const Obj, kwargs: *mut Map)
|
||||
let title: StrBuffer = kwargs.get(Qstr::MP_QSTR_title)?.try_into()?;
|
||||
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let words_iterable: Obj = kwargs.get(Qstr::MP_QSTR_words)?;
|
||||
let words: [StrBuffer; 3] = util::iter_into_array(words_iterable)?;
|
||||
let words: [TString<'static>; 3] = util::iter_into_array(words_iterable)?;
|
||||
|
||||
let paragraphs = Paragraphs::new([Paragraph::new(&theme::TEXT_DEMIBOLD, description)]);
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(paragraphs, Button::select_word(words)),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1336,8 +1335,8 @@ extern "C" fn new_show_share_words(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
title.into(),
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_hold()?
|
||||
.without_cancel(),
|
||||
))?;
|
||||
@ -1366,7 +1365,7 @@ extern "C" fn new_request_number(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
NumberInputDialog::new(min_count, max_count, count, callback)?,
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1394,7 +1393,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(
|
||||
Checklist::from_paragraphs(
|
||||
theme::ICON_LIST_CURRENT,
|
||||
@ -1407,7 +1406,7 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
.with_check_width(theme::CHECKLIST_CHECK_WIDTH)
|
||||
.with_current_offset(theme::CHECKLIST_CURRENT_OFFSET)
|
||||
.with_done_offset(theme::CHECKLIST_DONE_OFFSET),
|
||||
theme::button_bar(Button::with_text(button).map(|msg| {
|
||||
theme::button_bar(Button::with_text(button.into()).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
),
|
||||
@ -1440,20 +1439,23 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
||||
let obj = if info_button {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
notification,
|
||||
notification.into(),
|
||||
Dialog::new(
|
||||
paragraphs,
|
||||
Button::<StrBuffer>::cancel_info_confirm(
|
||||
TR::buttons__continue.try_into()?,
|
||||
TR::buttons__more_info.try_into()?,
|
||||
Button::cancel_info_confirm(
|
||||
TR::buttons__continue.into(),
|
||||
TR::buttons__more_info.into(),
|
||||
),
|
||||
),
|
||||
))?
|
||||
} else {
|
||||
LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
notification,
|
||||
Dialog::new(paragraphs, Button::cancel_confirm_text(None, Some(button))),
|
||||
notification.into(),
|
||||
Dialog::new(
|
||||
paragraphs,
|
||||
Button::cancel_confirm_text(None, Some(button.into())),
|
||||
),
|
||||
))?
|
||||
};
|
||||
Ok(obj.into())
|
||||
@ -1477,7 +1479,7 @@ extern "C" fn new_select_word_count(n_args: usize, args: *const Obj, kwargs: *mu
|
||||
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
title,
|
||||
title.into(),
|
||||
Dialog::new(paragraphs, SelectWordCount::new()),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
@ -1496,11 +1498,9 @@ extern "C" fn new_show_group_share_success(
|
||||
|
||||
let obj = LayoutObj::new(IconDialog::new_shares(
|
||||
lines,
|
||||
theme::button_bar(
|
||||
Button::<StrBuffer>::with_text(TR::buttons__continue.try_into()?).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
}),
|
||||
),
|
||||
theme::button_bar(Button::with_text(TR::buttons__continue.into()).map(|msg| {
|
||||
(matches!(msg, ButtonMsg::Clicked)).then(|| CancelConfirmMsg::Confirmed)
|
||||
})),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
@ -1519,12 +1519,11 @@ extern "C" fn new_show_remaining_shares(n_args: usize, args: *const Obj, kwargs:
|
||||
.add(Paragraph::new(&theme::TEXT_NORMAL, description).break_after());
|
||||
}
|
||||
|
||||
let tr_title: StrBuffer = TR::recovery__title_remaining_shares.try_into()?;
|
||||
let obj = LayoutObj::new(Frame::left_aligned(
|
||||
theme::label_title(),
|
||||
tr_title,
|
||||
ButtonPage::<_, StrBuffer>::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(TR::buttons__continue.try_into()?))
|
||||
TR::recovery__title_remaining_shares.into(),
|
||||
ButtonPage::new(paragraphs.into_paragraphs(), theme::BG)
|
||||
.with_cancel_confirm(None, Some(TR::buttons__continue.into()))
|
||||
.with_confirm_style(theme::button_default())
|
||||
.without_cancel(),
|
||||
))?;
|
||||
@ -1550,7 +1549,11 @@ extern "C" fn new_show_progress(n_args: usize, args: *const Obj, kwargs: *mut Ma
|
||||
|
||||
// Description updates are received as &str and we need to provide a way to
|
||||
// convert them to StrBuffer.
|
||||
let obj = LayoutObj::new(Progress::new(title, indeterminate, description.into()))?;
|
||||
let obj = LayoutObj::new(Progress::new(
|
||||
title.into(),
|
||||
indeterminate,
|
||||
description.into(),
|
||||
))?;
|
||||
Ok(obj.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@ -1565,7 +1568,7 @@ extern "C" fn new_show_progress_coinjoin(n_args: usize, args: *const Obj, kwargs
|
||||
|
||||
// The second type parameter is actually not used in `new()` but we need to
|
||||
// provide it.
|
||||
let progress = CoinJoinProgress::<_, Never>::new(title, indeterminate)?;
|
||||
let progress = CoinJoinProgress::<Never>::new(title.into(), indeterminate)?;
|
||||
let obj = if time_ms > 0 && indeterminate {
|
||||
let timeout = Timeout::new(time_ms);
|
||||
LayoutObj::new((timeout, progress.map(|_msg| None)))?
|
||||
@ -1642,19 +1645,17 @@ extern "C" fn new_confirm_firmware_update(
|
||||
let description: StrBuffer = kwargs.get(Qstr::MP_QSTR_description)?.try_into()?;
|
||||
let fingerprint: StrBuffer = kwargs.get(Qstr::MP_QSTR_fingerprint)?.try_into()?;
|
||||
|
||||
let title_str = TR::firmware_update__title.try_into()?;
|
||||
let title_str = TR::firmware_update__title.into();
|
||||
let title = Label::left_aligned(title_str, theme::TEXT_BOLD).vertically_centered();
|
||||
let msg = Label::left_aligned(description, theme::TEXT_NORMAL);
|
||||
let msg = Label::left_aligned(description.into(), theme::TEXT_NORMAL);
|
||||
|
||||
let left =
|
||||
Button::with_text(TR::buttons__cancel.try_into()?).styled(theme::button_default());
|
||||
let right =
|
||||
Button::with_text(TR::buttons__install.try_into()?).styled(theme::button_confirm());
|
||||
let left = Button::with_text(TR::buttons__cancel.into()).styled(theme::button_default());
|
||||
let right = Button::with_text(TR::buttons__install.into()).styled(theme::button_confirm());
|
||||
|
||||
let obj = LayoutObj::new(
|
||||
Confirm::new(theme::BG, left, right, ConfirmTitle::Text(title), msg).with_info(
|
||||
TR::firmware_update__title_fingerprint.try_into()?,
|
||||
fingerprint,
|
||||
TR::firmware_update__title_fingerprint.into(),
|
||||
fingerprint.into(),
|
||||
theme::button_moreinfo(),
|
||||
),
|
||||
)?;
|
||||
@ -2190,8 +2191,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn trace_example_layout() {
|
||||
let buttons =
|
||||
Button::cancel_confirm(Button::with_text("Left"), Button::with_text("Right"), false);
|
||||
let buttons = Button::cancel_confirm(
|
||||
Button::with_text("Left".into()),
|
||||
Button::with_text("Right".into()),
|
||||
false,
|
||||
);
|
||||
|
||||
let ops = OpTextLayout::new(theme::TEXT_NORMAL)
|
||||
.text_normal("Testing text layout, with some text, and some more text. And ")
|
||||
|
@ -1,5 +1,3 @@
|
||||
#[cfg(feature = "micropython")]
|
||||
use crate::micropython::buffer::StrBuffer;
|
||||
use crate::ui::{
|
||||
component::Component,
|
||||
constant::screen,
|
||||
@ -10,26 +8,8 @@ use crate::ui::{
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "micropython"))]
|
||||
// SAFETY: Actually safe but see below
|
||||
unsafe fn get_str(text: &str) -> &str {
|
||||
text
|
||||
}
|
||||
#[cfg(feature = "micropython")]
|
||||
// SAFETY: The caller is responsible for ensuring that the StrBuffer does not
|
||||
// escape the lifetime of the original &str.
|
||||
unsafe fn get_str(text: &str) -> StrBuffer {
|
||||
unsafe { StrBuffer::from_ptr_and_len(text.as_ptr(), text.len()) }
|
||||
}
|
||||
|
||||
pub fn screen_fatal_error(title: &str, msg: &str, footer: &str) {
|
||||
// SAFETY: these will get placed into `frame` which does not outlive this
|
||||
// function
|
||||
let title = unsafe { get_str(title) };
|
||||
let msg = unsafe { get_str(msg) };
|
||||
let footer = unsafe { get_str(footer) };
|
||||
|
||||
let mut frame = ErrorScreen::new(title, msg, footer);
|
||||
let mut frame = ErrorScreen::new(title.into(), msg.into(), footer.into());
|
||||
frame.place(constant::screen());
|
||||
frame.paint();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user