feat(core/rust): improve and use coinjoin loading screens

[no changelog]
mmilata/u5/flash
grdddj 12 months ago committed by Jiří Musil
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, &notification.as_ref(), Font::MONO);
display_center(baseline, &notification.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…
Cancel
Save