You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/rust/src/ui/model_mercury/component/result.rs

201 lines
5.1 KiB

use crate::{
strutil::StringType,
ui::{
component::{text::TextStyle, Child, Component, Event, EventCtx, Label, Never, Pad},
constant::screen,
display::{self, Color, Font, Icon},
geometry::{Alignment2D, Insets, Offset, Point, Rect},
model_mercury::theme::FG,
shape,
shape::Renderer,
},
};
use crate::ui::model_mercury::{
constant::WIDTH,
theme::{RESULT_FOOTER_START, RESULT_PADDING},
};
const MESSAGE_AREA_START: i16 = 97;
const ICON_CENTER_Y: i16 = 62;
pub struct ResultStyle {
pub fg_color: Color,
pub bg_color: Color,
pub divider_color: Color,
}
impl ResultStyle {
pub const fn new(fg_color: Color, bg_color: Color, divider_color: Color) -> Self {
Self {
fg_color,
bg_color,
divider_color,
}
}
pub const fn message_style(&self) -> TextStyle {
TextStyle::new(Font::NORMAL, self.fg_color, self.bg_color, FG, FG)
}
pub const fn title_style(&self) -> TextStyle {
TextStyle::new(Font::BOLD, self.fg_color, self.bg_color, FG, FG)
}
}
pub struct ResultFooter<'a, T> {
style: &'a ResultStyle,
text: Label<T>,
area: Rect,
}
impl<'a, T: AsRef<str>> ResultFooter<'a, T> {
pub fn new(text: Label<T>, style: &'a ResultStyle) -> Self {
Self {
style,
text,
area: Rect::zero(),
}
}
pub const fn split_bounds() -> (Rect, Rect) {
let main_area = Rect::new(
Point::new(RESULT_PADDING, 0),
Point::new(WIDTH - RESULT_PADDING, RESULT_FOOTER_START),
);
let footer_area = Rect::new(
Point::new(RESULT_PADDING, RESULT_FOOTER_START),
Point::new(WIDTH - RESULT_PADDING, screen().height()),
);
(main_area, footer_area)
}
}
impl<T: AsRef<str>> Component for ResultFooter<'_, T> {
type Msg = Never;
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
self.text.place(bounds);
bounds
}
fn paint(&mut self) {
// divider line
let bar = Rect::from_center_and_size(
Point::new(self.area.center().x, self.area.y0),
Offset::new(self.area.width(), 1),
);
display::rect_fill(bar, self.style.divider_color);
// footer text
self.text.paint();
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
// divider line
let bar = Rect::from_center_and_size(
Point::new(self.area.center().x, self.area.y0),
Offset::new(self.area.width(), 1),
);
shape::Bar::new(bar)
.with_fg(self.style.divider_color)
.render(target);
// footer text
self.text.render(target);
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
}
pub struct ResultScreen<'a, T> {
bg: Pad,
footer_pad: Pad,
style: &'a ResultStyle,
icon: Icon,
message: Child<Label<T>>,
footer: Child<ResultFooter<'a, &'a str>>,
}
impl<'a, T: StringType> ResultScreen<'a, T> {
pub fn new(
style: &'a ResultStyle,
icon: Icon,
message: T,
footer: Label<&'a str>,
complete_draw: bool,
) -> Self {
let mut instance = Self {
bg: Pad::with_background(style.bg_color),
footer_pad: Pad::with_background(style.bg_color),
style,
icon,
message: Child::new(Label::centered(message, style.message_style())),
footer: Child::new(ResultFooter::new(footer, style)),
};
if complete_draw {
instance.bg.clear();
} else {
instance.footer_pad.clear();
}
instance
}
}
impl<'a, T: StringType> Component for ResultScreen<'a, T> {
type Msg = Never;
fn place(&mut self, _bounds: Rect) -> Rect {
self.bg.place(screen());
let (main_area, footer_area) = ResultFooter::<&'a str>::split_bounds();
self.footer_pad.place(footer_area);
self.footer.place(footer_area);
let message_area = main_area.inset(Insets::top(MESSAGE_AREA_START));
self.message.place(message_area);
screen()
}
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
None
}
fn paint(&mut self) {
self.bg.paint();
self.footer_pad.paint();
self.icon.draw(
Point::new(screen().center().x, ICON_CENTER_Y),
Alignment2D::CENTER,
self.style.fg_color,
self.style.bg_color,
);
self.message.paint();
self.footer.paint();
}
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.bg.render(target);
self.footer_pad.render(target);
shape::ToifImage::new(
Point::new(screen().center().x, ICON_CENTER_Y),
self.icon.toif,
)
.with_align(Alignment2D::CENTER)
.with_fg(self.style.fg_color)
.with_bg(self.style.bg_color)
.render(target);
self.message.render(target);
self.footer.render(target);
}
}