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 does not fit, returns `None`.
|
||||
pub fn text_multiline_split_words(
|
||||
pub fn text_multiline(
|
||||
area: Rect,
|
||||
text: &str,
|
||||
font: Font,
|
||||
@ -33,3 +33,33 @@ pub fn text_multiline_split_words(
|
||||
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,
|
||||
ui::{
|
||||
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,
|
||||
geometry::{Alignment, Rect},
|
||||
geometry::{Alignment, Insets, Rect},
|
||||
},
|
||||
};
|
||||
|
||||
use super::theme;
|
||||
|
||||
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> {
|
||||
text: T,
|
||||
area: Rect,
|
||||
indeterminate: bool,
|
||||
}
|
||||
|
||||
impl<T> CoinJoinProgress<T>
|
||||
where
|
||||
T: StringType,
|
||||
{
|
||||
pub fn new(text: T, _indeterminate: bool) -> Self {
|
||||
pub fn new(text: T, indeterminate: bool) -> Self {
|
||||
Self {
|
||||
text,
|
||||
area: Rect::zero(),
|
||||
indeterminate,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,33 +52,34 @@ where
|
||||
}
|
||||
|
||||
fn paint(&mut self) {
|
||||
// Trying to paint all three parts into the area, stopping if any of them
|
||||
// doesn't fit.
|
||||
let mut possible_rest = text_multiline_split_words(
|
||||
// TOP
|
||||
if self.indeterminate {
|
||||
text_multiline(
|
||||
self.area,
|
||||
HEADER,
|
||||
Font::BOLD,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
Alignment::Center,
|
||||
);
|
||||
}
|
||||
|
||||
// CENTER
|
||||
|
||||
// BOTTOM
|
||||
let top_rest = text_multiline_bottom(
|
||||
self.area,
|
||||
HEADER,
|
||||
FOOTER,
|
||||
Font::BOLD,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
Alignment::Center,
|
||||
);
|
||||
if let Some(rest) = possible_rest {
|
||||
possible_rest = text_multiline_split_words(
|
||||
rest,
|
||||
if let Some(rest) = top_rest {
|
||||
text_multiline_bottom(
|
||||
rest.inset(Insets::bottom(FOOTER_TEXT_MARGIN)),
|
||||
self.text.as_ref(),
|
||||
Font::MONO,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
Alignment::Center,
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if let Some(rest) = possible_rest {
|
||||
text_multiline_split_words(
|
||||
rest,
|
||||
FOOTER,
|
||||
Font::BOLD,
|
||||
Font::NORMAL,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
Alignment::Center,
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
trezorhal::usb::usb_configured,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label},
|
||||
display::{rect_fill, toif::Toif, Font},
|
||||
display::{rect_fill, toif::Toif, Font, Icon},
|
||||
event::USBEvent,
|
||||
geometry::{Alignment2D, Insets, Offset, Point, Rect},
|
||||
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 NOTIFICATION_HEIGHT: i16 = 12;
|
||||
const LABEL_OUTSET: i16 = 3;
|
||||
const NOTIFICATION_FONT: Font = Font::NORMAL;
|
||||
const NOTIFICATION_ICON: Icon = theme::ICON_WARNING;
|
||||
|
||||
pub struct Homescreen<T>
|
||||
where
|
||||
@ -66,16 +68,32 @@ where
|
||||
}
|
||||
|
||||
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() {
|
||||
self.fill_notification_background();
|
||||
// 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 {
|
||||
self.fill_notification_background();
|
||||
// TODO: what if the notification text is so long it collides with icons?
|
||||
self.paint_warning_icons_in_top_corners();
|
||||
display_center(baseline, ¬ification.as_ref(), Font::MONO);
|
||||
display_center(baseline, ¬ification.as_ref(), 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());
|
||||
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,
|
||||
ui::{
|
||||
component::{
|
||||
text::util::text_multiline_split_words, Child, Component, Event, EventCtx, Never,
|
||||
Paginate,
|
||||
text::util::text_multiline, Child, Component, Event, EventCtx, Never, Paginate,
|
||||
},
|
||||
display::Font,
|
||||
geometry::{Alignment, Offset, Rect},
|
||||
@ -118,7 +117,7 @@ where
|
||||
|
||||
/// Shows text in the main screen area.
|
||||
fn render_text_on_screen(&self, text: &str, font: Font) {
|
||||
text_multiline_split_words(
|
||||
text_multiline(
|
||||
self.area.split_top(INFO_TOP_OFFSET).1,
|
||||
text,
|
||||
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_feerate: StrBuffer = kwargs.get(Qstr::MP_QSTR_max_feerate)?.try_into()?;
|
||||
|
||||
// Decreasing bottom padding between paragraphs to fit one screen
|
||||
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_BOLD, "Max mining fee".into()).no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, max_feerate),
|
||||
Paragraph::new(&theme::TEXT_BOLD, "Max mining fee".into())
|
||||
.with_bottom_padding(2)
|
||||
.no_break(),
|
||||
Paragraph::new(&theme::TEXT_MONO, max_feerate).with_bottom_padding(2),
|
||||
]);
|
||||
|
||||
content_in_button_page(
|
||||
|
@ -272,7 +272,7 @@ async def handle_UnlockPath(msg: UnlockPath) -> protobuf.MessageType:
|
||||
"confirm_coinjoin_access",
|
||||
title="Coinjoin",
|
||||
description="Access your coinjoin account?",
|
||||
verb="ALLOW",
|
||||
verb="ACCESS",
|
||||
)
|
||||
|
||||
wire_types = (MessageType.GetAddress, MessageType.GetPublicKey, MessageType.SignTx)
|
||||
|
@ -24,7 +24,6 @@ async def homescreen() -> None:
|
||||
notification = None
|
||||
notification_is_error = False
|
||||
if is_set_any_session(MessageType.AuthorizeCoinJoin):
|
||||
# TODO: is too long for TR
|
||||
notification = "COINJOIN AUTHORIZED"
|
||||
elif storage.device.is_initialized() and storage.device.no_backup():
|
||||
notification = "SEEDLESS"
|
||||
@ -37,7 +36,6 @@ async def homescreen() -> None:
|
||||
elif storage.device.is_initialized() and not config.has_pin():
|
||||
notification = "PIN NOT SET"
|
||||
elif storage.device.get_experimental_features():
|
||||
# TODO: is too long for TR
|
||||
notification = "EXPERIMENTAL MODE"
|
||||
|
||||
await Homescreen(
|
||||
|
@ -49,11 +49,9 @@ def bitcoin_progress(description: str) -> ProgressLayout:
|
||||
|
||||
|
||||
def coinjoin_progress(message: str) -> ProgressLayout:
|
||||
# TODO: create show_progress_coinjoin for TR
|
||||
return progress("Coinjoin", message)
|
||||
# return RustProgress(
|
||||
# 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:
|
||||
|
Loading…
Reference in New Issue
Block a user