mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-20 06:28:09 +00:00
feat(core/rust): improve and use coinjoin loading screens
[no changelog]
This commit is contained in:
parent
6a86527882
commit
b96b9d43bb
@ -15,7 +15,7 @@ use super::{
|
|||||||
///
|
///
|
||||||
/// If it fits, returns the rest of the area.
|
/// If it fits, returns the rest of the area.
|
||||||
/// If it does not fit, returns `None`.
|
/// If it does not fit, returns `None`.
|
||||||
pub fn text_multiline_split_words(
|
pub fn text_multiline(
|
||||||
area: Rect,
|
area: Rect,
|
||||||
text: &str,
|
text: &str,
|
||||||
font: Font,
|
font: Font,
|
||||||
@ -33,3 +33,33 @@ pub fn text_multiline_split_words(
|
|||||||
LayoutFit::OutOfBounds { .. } => None,
|
LayoutFit::OutOfBounds { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as `text_multiline` above, but aligns the text to the bottom of the
|
||||||
|
/// area.
|
||||||
|
pub fn text_multiline_bottom(
|
||||||
|
area: Rect,
|
||||||
|
text: &str,
|
||||||
|
font: Font,
|
||||||
|
fg_color: Color,
|
||||||
|
bg_color: Color,
|
||||||
|
alignment: Alignment,
|
||||||
|
) -> Option<Rect> {
|
||||||
|
let text_style = TextStyle::new(font, fg_color, bg_color, fg_color, fg_color);
|
||||||
|
let mut text_layout = TextLayout::new(text_style)
|
||||||
|
.with_bounds(area)
|
||||||
|
.with_align(alignment);
|
||||||
|
// When text fits the area, displaying it in the bottom part.
|
||||||
|
// When not, render it "normally".
|
||||||
|
match text_layout.fit_text(text) {
|
||||||
|
LayoutFit::Fitting { height, .. } => {
|
||||||
|
let (top, bottom) = area.split_bottom(height);
|
||||||
|
text_layout = text_layout.with_bounds(bottom);
|
||||||
|
text_layout.render_text(text);
|
||||||
|
Some(top)
|
||||||
|
}
|
||||||
|
LayoutFit::OutOfBounds { .. } => {
|
||||||
|
text_layout.render_text(text);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,31 +2,36 @@ use crate::{
|
|||||||
strutil::StringType,
|
strutil::StringType,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
base::Never, text::util::text_multiline_split_words, Component, Event, EventCtx,
|
base::Never,
|
||||||
|
text::util::{text_multiline, text_multiline_bottom},
|
||||||
|
Component, Event, EventCtx,
|
||||||
},
|
},
|
||||||
display::Font,
|
display::Font,
|
||||||
geometry::{Alignment, Rect},
|
geometry::{Alignment, Insets, Rect},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::theme;
|
use super::theme;
|
||||||
|
|
||||||
const HEADER: &str = "COINJOIN IN PROGRESS";
|
const HEADER: &str = "COINJOIN IN PROGRESS";
|
||||||
const FOOTER: &str = "Don't disconnect your Trezor";
|
const FOOTER: &str = "Do not disconnect your Trezor!";
|
||||||
|
const FOOTER_TEXT_MARGIN: i16 = 8;
|
||||||
|
|
||||||
pub struct CoinJoinProgress<T> {
|
pub struct CoinJoinProgress<T> {
|
||||||
text: T,
|
text: T,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
|
indeterminate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> CoinJoinProgress<T>
|
impl<T> CoinJoinProgress<T>
|
||||||
where
|
where
|
||||||
T: StringType,
|
T: StringType,
|
||||||
{
|
{
|
||||||
pub fn new(text: T, _indeterminate: bool) -> Self {
|
pub fn new(text: T, indeterminate: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
|
indeterminate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,33 +52,34 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
// Trying to paint all three parts into the area, stopping if any of them
|
// TOP
|
||||||
// doesn't fit.
|
if self.indeterminate {
|
||||||
let mut possible_rest = text_multiline_split_words(
|
text_multiline(
|
||||||
|
self.area,
|
||||||
|
HEADER,
|
||||||
|
Font::BOLD,
|
||||||
|
theme::FG,
|
||||||
|
theme::BG,
|
||||||
|
Alignment::Center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CENTER
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
let top_rest = text_multiline_bottom(
|
||||||
self.area,
|
self.area,
|
||||||
HEADER,
|
FOOTER,
|
||||||
Font::BOLD,
|
Font::BOLD,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
Alignment::Center,
|
Alignment::Center,
|
||||||
);
|
);
|
||||||
if let Some(rest) = possible_rest {
|
if let Some(rest) = top_rest {
|
||||||
possible_rest = text_multiline_split_words(
|
text_multiline_bottom(
|
||||||
rest,
|
rest.inset(Insets::bottom(FOOTER_TEXT_MARGIN)),
|
||||||
self.text.as_ref(),
|
self.text.as_ref(),
|
||||||
Font::MONO,
|
Font::NORMAL,
|
||||||
theme::FG,
|
|
||||||
theme::BG,
|
|
||||||
Alignment::Center,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(rest) = possible_rest {
|
|
||||||
text_multiline_split_words(
|
|
||||||
rest,
|
|
||||||
FOOTER,
|
|
||||||
Font::BOLD,
|
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
Alignment::Center,
|
Alignment::Center,
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
trezorhal::usb::usb_configured,
|
trezorhal::usb::usb_configured,
|
||||||
ui::{
|
ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Label},
|
component::{Child, Component, Event, EventCtx, Label},
|
||||||
display::{rect_fill, toif::Toif, Font},
|
display::{rect_fill, toif::Toif, Font, Icon},
|
||||||
event::USBEvent,
|
event::USBEvent,
|
||||||
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||||
layout::util::get_user_custom_image,
|
layout::util::get_user_custom_image,
|
||||||
@ -25,6 +25,8 @@ const LOGO_ICON_TOP_MARGIN: i16 = 12;
|
|||||||
const LOCK_ICON_TOP_MARGIN: i16 = 12;
|
const LOCK_ICON_TOP_MARGIN: i16 = 12;
|
||||||
const NOTIFICATION_HEIGHT: i16 = 12;
|
const NOTIFICATION_HEIGHT: i16 = 12;
|
||||||
const LABEL_OUTSET: i16 = 3;
|
const LABEL_OUTSET: i16 = 3;
|
||||||
|
const NOTIFICATION_FONT: Font = Font::NORMAL;
|
||||||
|
const NOTIFICATION_ICON: Icon = theme::ICON_WARNING;
|
||||||
|
|
||||||
pub struct Homescreen<T>
|
pub struct Homescreen<T>
|
||||||
where
|
where
|
||||||
@ -66,16 +68,32 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint_notification(&self) {
|
fn paint_notification(&self) {
|
||||||
let baseline = TOP_CENTER + Offset::y(Font::MONO.line_height());
|
let baseline = TOP_CENTER + Offset::y(NOTIFICATION_FONT.line_height());
|
||||||
if !usb_configured() {
|
if !usb_configured() {
|
||||||
self.fill_notification_background();
|
self.fill_notification_background();
|
||||||
// TODO: fill warning icons here as well?
|
// TODO: fill warning icons here as well?
|
||||||
display_center(baseline, &"NO USB CONNECTION", Font::MONO);
|
display_center(baseline, &"NO USB CONNECTION", NOTIFICATION_FONT);
|
||||||
} else if let Some((notification, _level)) = &self.notification {
|
} else if let Some((notification, _level)) = &self.notification {
|
||||||
self.fill_notification_background();
|
self.fill_notification_background();
|
||||||
// TODO: what if the notification text is so long it collides with icons?
|
display_center(baseline, ¬ification.as_ref(), NOTIFICATION_FONT);
|
||||||
self.paint_warning_icons_in_top_corners();
|
// Painting warning icons in top corners when the text is short enough not to
|
||||||
display_center(baseline, ¬ification.as_ref(), Font::MONO);
|
// collide with them
|
||||||
|
let icon_width = NOTIFICATION_ICON.toif.width();
|
||||||
|
let text_width = NOTIFICATION_FONT.text_width(notification.as_ref());
|
||||||
|
if AREA.width() >= text_width + (icon_width + 1) * 2 {
|
||||||
|
NOTIFICATION_ICON.draw(
|
||||||
|
AREA.top_left(),
|
||||||
|
Alignment2D::TOP_LEFT,
|
||||||
|
theme::FG,
|
||||||
|
theme::BG,
|
||||||
|
);
|
||||||
|
NOTIFICATION_ICON.draw(
|
||||||
|
AREA.top_right(),
|
||||||
|
Alignment2D::TOP_RIGHT,
|
||||||
|
theme::FG,
|
||||||
|
theme::BG,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,7 @@ use crate::{
|
|||||||
strutil::StringType,
|
strutil::StringType,
|
||||||
ui::{
|
ui::{
|
||||||
component::{
|
component::{
|
||||||
text::util::text_multiline_split_words, Child, Component, Event, EventCtx, Never,
|
text::util::text_multiline, Child, Component, Event, EventCtx, Never, Paginate,
|
||||||
Paginate,
|
|
||||||
},
|
},
|
||||||
display::Font,
|
display::Font,
|
||||||
geometry::{Alignment, Offset, Rect},
|
geometry::{Alignment, Offset, Rect},
|
||||||
@ -118,7 +117,7 @@ where
|
|||||||
|
|
||||||
/// Shows text in the main screen area.
|
/// Shows text in the main screen area.
|
||||||
fn render_text_on_screen(&self, text: &str, font: Font) {
|
fn render_text_on_screen(&self, text: &str, font: Font) {
|
||||||
text_multiline_split_words(
|
text_multiline(
|
||||||
self.area.split_top(INFO_TOP_OFFSET).1,
|
self.area.split_top(INFO_TOP_OFFSET).1,
|
||||||
text,
|
text,
|
||||||
font,
|
font,
|
||||||
|
@ -958,11 +958,14 @@ extern "C" fn new_confirm_coinjoin(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
let max_rounds: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_rounds)?.try_into()?;
|
let max_rounds: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_rounds)?.try_into()?;
|
||||||
let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
|
let max_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
|
||||||
|
|
||||||
|
// Decreasing bottom padding between paragraphs to fit one screen
|
||||||
let paragraphs = Paragraphs::new([
|
let paragraphs = Paragraphs::new([
|
||||||
Paragraph::new(&theme::TEXT_BOLD, "Max rounds".into()),
|
Paragraph::new(&theme::TEXT_BOLD, "Max rounds".into()).with_bottom_padding(2),
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
Paragraph::new(&theme::TEXT_MONO, max_rounds),
|
||||||
Paragraph::new(&theme::TEXT_BOLD, "Max mining fee".into()).no_break(),
|
Paragraph::new(&theme::TEXT_BOLD, "Max mining fee".into())
|
||||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
.with_bottom_padding(2)
|
||||||
|
.no_break(),
|
||||||
|
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
content_in_button_page(
|
content_in_button_page(
|
||||||
|
@ -272,7 +272,7 @@ async def handle_UnlockPath(msg: UnlockPath) -> protobuf.MessageType:
|
|||||||
"confirm_coinjoin_access",
|
"confirm_coinjoin_access",
|
||||||
title="Coinjoin",
|
title="Coinjoin",
|
||||||
description="Access your coinjoin account?",
|
description="Access your coinjoin account?",
|
||||||
verb="ALLOW",
|
verb="ACCESS",
|
||||||
)
|
)
|
||||||
|
|
||||||
wire_types = (MessageType.GetAddress, MessageType.GetPublicKey, MessageType.SignTx)
|
wire_types = (MessageType.GetAddress, MessageType.GetPublicKey, MessageType.SignTx)
|
||||||
|
@ -24,7 +24,6 @@ async def homescreen() -> None:
|
|||||||
notification = None
|
notification = None
|
||||||
notification_is_error = False
|
notification_is_error = False
|
||||||
if is_set_any_session(MessageType.AuthorizeCoinJoin):
|
if is_set_any_session(MessageType.AuthorizeCoinJoin):
|
||||||
# TODO: is too long for TR
|
|
||||||
notification = "COINJOIN AUTHORIZED"
|
notification = "COINJOIN AUTHORIZED"
|
||||||
elif storage.device.is_initialized() and storage.device.no_backup():
|
elif storage.device.is_initialized() and storage.device.no_backup():
|
||||||
notification = "SEEDLESS"
|
notification = "SEEDLESS"
|
||||||
@ -37,7 +36,6 @@ async def homescreen() -> None:
|
|||||||
elif storage.device.is_initialized() and not config.has_pin():
|
elif storage.device.is_initialized() and not config.has_pin():
|
||||||
notification = "PIN NOT SET"
|
notification = "PIN NOT SET"
|
||||||
elif storage.device.get_experimental_features():
|
elif storage.device.get_experimental_features():
|
||||||
# TODO: is too long for TR
|
|
||||||
notification = "EXPERIMENTAL MODE"
|
notification = "EXPERIMENTAL MODE"
|
||||||
|
|
||||||
await Homescreen(
|
await Homescreen(
|
||||||
|
@ -49,11 +49,9 @@ def bitcoin_progress(description: str) -> ProgressLayout:
|
|||||||
|
|
||||||
|
|
||||||
def coinjoin_progress(message: str) -> ProgressLayout:
|
def coinjoin_progress(message: str) -> ProgressLayout:
|
||||||
# TODO: create show_progress_coinjoin for TR
|
return RustProgress(
|
||||||
return progress("Coinjoin", message)
|
layout=trezorui2.show_progress_coinjoin(title=message, indeterminate=False)
|
||||||
# return RustProgress(
|
)
|
||||||
# layout=trezorui2.show_progress_coinjoin(title=message, indeterminate=False)
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
def pin_progress(message: str, description: str) -> ProgressLayout:
|
def pin_progress(message: str, description: str) -> ProgressLayout:
|
||||||
|
Loading…
Reference in New Issue
Block a user