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_tr/component/result_popup.rs

194 lines
5.5 KiB

use crate::{
time::Instant,
ui::{
component::{
text::{layout::DefaultTextTheme, paragraphs::Paragraphs},
Child, Component, ComponentExt, Event, EventCtx, Label, LabelStyle, Pad,
},
constant::screen,
display::{Color, Font},
geometry::{Alignment, Insets, LinearPlacement, Point, Rect},
model_tr::{
component::{Button, ButtonMsg, ButtonPos, ResultAnim, ResultAnimMsg},
theme,
theme::{TRDefaultText, FONT_BOLD, FONT_MEDIUM},
},
},
};
pub enum ResultPopupMsg {
Confirmed,
}
pub struct ResultPopup {
area: Rect,
pad: Pad,
result_anim: Child<ResultAnim>,
headline_baseline: Point,
headline: Option<Label<&'static str>>,
text: Child<Paragraphs<&'static str>>,
button: Option<Child<Button<&'static str>>>,
autoclose: bool,
}
pub struct MessageText;
impl DefaultTextTheme for MessageText {
const BACKGROUND_COLOR: Color = theme::BG;
const TEXT_FONT: Font = FONT_MEDIUM;
const TEXT_COLOR: Color = theme::FG;
const HYPHEN_FONT: Font = FONT_MEDIUM;
const HYPHEN_COLOR: Color = theme::FG;
const ELLIPSIS_FONT: Font = FONT_MEDIUM;
const ELLIPSIS_COLOR: Color = theme::FG;
const NORMAL_FONT: Font = FONT_MEDIUM;
const MEDIUM_FONT: Font = theme::FONT_MEDIUM;
const BOLD_FONT: Font = theme::FONT_BOLD;
const MONO_FONT: Font = theme::FONT_MONO;
}
const ANIM_SIZE: i32 = 18;
const BUTTON_HEIGHT: i32 = 13;
const ANIM_SPACE: i32 = 11;
const ANIM_POS: i32 = 32;
const ANIM_POS_ADJ_HEADLINE: i32 = 10;
const ANIM_POS_ADJ_BUTTON: i32 = 6;
impl ResultPopup {
pub fn new(
icon: &'static [u8],
text: &'static str,
headline: Option<&'static str>,
button_text: Option<&'static str>,
) -> Self {
let p1 = Paragraphs::new()
.add::<TRDefaultText>(FONT_MEDIUM, text)
.with_placement(LinearPlacement::vertical().align_at_center());
let button = button_text.map(|t| {
Child::new(Button::with_text(
ButtonPos::Right,
t,
theme::button_default(),
))
});
let headline_style = LabelStyle {
background_color: theme::BG,
text_color: theme::FG,
font: FONT_BOLD,
};
let mut pad = Pad::with_background(theme::BG);
pad.clear();
Self {
area: Rect::zero(),
pad,
result_anim: Child::new(ResultAnim::new(icon)),
headline: headline.map(|a| Label::new(a, Alignment::Center, headline_style)),
headline_baseline: Point::zero(),
text: Child::new(p1),
button,
autoclose: false,
}
}
// autoclose even if button is used
pub fn autoclose(&mut self) {
self.autoclose = true;
}
pub fn start(&mut self, ctx: &mut EventCtx) {
self.text.request_complete_repaint(ctx);
self.headline.request_complete_repaint(ctx);
self.button.request_complete_repaint(ctx);
self.result_anim.mutate(ctx, |ctx, c| {
let now = Instant::now();
c.start_growing(ctx, now);
});
ctx.request_paint();
}
}
impl Component for ResultPopup {
type Msg = ResultPopupMsg;
fn place(&mut self, bounds: Rect) -> Rect {
self.area = bounds;
let anim_margins = (screen().width() - ANIM_SIZE) / 2;
let mut anim_adjust = 0;
let mut headline_height = 0;
let mut button_height = 0;
if let Some(h) = self.headline.as_mut() {
headline_height = h.size().y;
anim_adjust += ANIM_POS_ADJ_HEADLINE;
}
if self.button.is_some() {
button_height = BUTTON_HEIGHT;
anim_adjust += ANIM_POS_ADJ_BUTTON;
}
let (_, rest) = bounds.split_top(ANIM_POS - anim_adjust);
let (anim, rest) = rest.split_top(ANIM_SIZE);
let (_, rest) = rest.split_top(ANIM_SPACE);
let (headline, rest) = rest.split_top(headline_height);
let (text, buttons) = rest.split_bottom(button_height);
self.pad.place(bounds);
self.button.place(buttons);
self.headline.place(headline);
self.text.place(text);
self.result_anim
.place(anim.inset(Insets::sides(anim_margins)));
self.area
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
let mut button_confirmed = false;
self.text.event(ctx, event);
self.headline.event(ctx, event);
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
button_confirmed = true;
}
if let Some(ResultAnimMsg::FullyGrown) = self.result_anim.event(ctx, event) {
if self.button.is_none() || self.autoclose {
return Some(ResultPopupMsg::Confirmed);
}
}
if button_confirmed {
return Some(ResultPopupMsg::Confirmed);
}
None
}
fn paint(&mut self) {
self.pad.paint();
self.text.paint();
self.button.paint();
self.headline.paint();
self.result_anim.paint();
}
}
#[cfg(feature = "ui_debug")]
impl crate::trace::Trace for ResultPopup {
fn trace(&self, d: &mut dyn crate::trace::Tracer) {
d.open("ResultPopup");
self.text.trace(d);
self.button.trace(d);
self.headline.trace(d);
self.result_anim.trace(d);
d.close();
}
}