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.
201 lines
5.1 KiB
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);
|
|
}
|
|
}
|