mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 22:26:08 +00:00
feat(eckhart): implement bootloader loader
This commit is contained in:
parent
b6f27f3087
commit
942e3953e2
@ -1,40 +1,96 @@
|
|||||||
use crate::ui::{display::Color, geometry::Point, shape, shape::Renderer};
|
use crate::ui::{
|
||||||
|
constant::SCREEN,
|
||||||
|
geometry::{Offset, Rect},
|
||||||
|
lerp::Lerp,
|
||||||
|
shape::{self, Renderer},
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::constant;
|
use super::{super::theme::BG, ScreenBorder};
|
||||||
|
|
||||||
pub enum LoaderRange {
|
|
||||||
Full,
|
|
||||||
FromTo(f32, f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_loader<'s>(
|
pub fn render_loader<'s>(
|
||||||
center: Point,
|
progress: u16,
|
||||||
inactive_color: Color,
|
border: &'static ScreenBorder,
|
||||||
active_color: Color,
|
|
||||||
background_color: Color,
|
|
||||||
range: LoaderRange,
|
|
||||||
target: &mut impl Renderer<'s>,
|
target: &mut impl Renderer<'s>,
|
||||||
) {
|
) {
|
||||||
shape::Circle::new(center, constant::LOADER_OUTER)
|
// convert to ration from 0.0 to 1.0
|
||||||
.with_bg(inactive_color)
|
let progress_ratio = (progress as f32 / 1000.0).clamp(0.0, 1.0);
|
||||||
.render(target);
|
let (clip, top_gap) = get_clips(progress_ratio);
|
||||||
|
render_clipped_border(border, clip, top_gap, u8::MAX, target);
|
||||||
|
}
|
||||||
|
|
||||||
match range {
|
fn get_clips(progress_ratio: f32) -> (Rect, Rect) {
|
||||||
LoaderRange::Full => {
|
/// Ratio of total_duration for the bottom part of the border
|
||||||
shape::Circle::new(center, constant::LOADER_OUTER)
|
const BOTTOM_DURATION_RATIO: f32 = 0.125;
|
||||||
.with_bg(active_color)
|
/// Ratio of total_duration for the side parts of the border
|
||||||
.render(target);
|
const SIDES_DURATION_RATIO: f32 = 0.5;
|
||||||
|
/// Ratio of total_duration for the top part of the border
|
||||||
|
const TOP_DURATION_RATIO: f32 = 0.375;
|
||||||
|
|
||||||
|
const TOP_GAP_ZERO: Rect = Rect::from_center_and_size(
|
||||||
|
SCREEN.top_center().ofs(Offset::y(ScreenBorder::WIDTH / 2)),
|
||||||
|
Offset::zero(),
|
||||||
|
);
|
||||||
|
const TOP_GAP_FULL: Rect = Rect::from_center_and_size(
|
||||||
|
SCREEN.top_center().ofs(Offset::y(ScreenBorder::WIDTH / 2)),
|
||||||
|
Offset::new(SCREEN.width(), ScreenBorder::WIDTH),
|
||||||
|
);
|
||||||
|
|
||||||
|
match progress_ratio {
|
||||||
|
// Bottom phase growing linearly
|
||||||
|
p if p < BOTTOM_DURATION_RATIO => {
|
||||||
|
let bottom_progress = (p / BOTTOM_DURATION_RATIO).clamp(0.0, 1.0);
|
||||||
|
let width = i16::lerp(0, SCREEN.width(), bottom_progress);
|
||||||
|
let clip = Rect::from_center_and_size(
|
||||||
|
SCREEN
|
||||||
|
.bottom_center()
|
||||||
|
.ofs(Offset::y(-ScreenBorder::WIDTH / 2)),
|
||||||
|
Offset::new(width, ScreenBorder::WIDTH),
|
||||||
|
);
|
||||||
|
(clip, TOP_GAP_FULL)
|
||||||
}
|
}
|
||||||
LoaderRange::FromTo(start, end) => {
|
|
||||||
shape::Circle::new(center, constant::LOADER_OUTER)
|
// Sides phase growing up linearly
|
||||||
.with_bg(active_color)
|
p if p < (BOTTOM_DURATION_RATIO + SIDES_DURATION_RATIO) => {
|
||||||
.with_start_angle(start)
|
let sides_progress =
|
||||||
.with_end_angle(end)
|
((p - BOTTOM_DURATION_RATIO) / SIDES_DURATION_RATIO).clamp(0.0, 1.0);
|
||||||
.render(target);
|
let height = i16::lerp(ScreenBorder::WIDTH, SCREEN.height(), sides_progress);
|
||||||
|
let clip = Rect::from_bottom_left_and_size(
|
||||||
|
SCREEN.bottom_left(),
|
||||||
|
Offset::new(SCREEN.width(), height),
|
||||||
|
);
|
||||||
|
(clip, TOP_GAP_FULL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Top gap shrinking linearly
|
||||||
|
p if p < 1.0 => {
|
||||||
|
let top_progress = ((p - BOTTOM_DURATION_RATIO - SIDES_DURATION_RATIO)
|
||||||
|
/ TOP_DURATION_RATIO)
|
||||||
|
.clamp(0.0, 1.0);
|
||||||
|
let width = i16::lerp(SCREEN.width(), 0, top_progress);
|
||||||
|
let top_gap = Rect::from_center_and_size(
|
||||||
|
SCREEN.top_center().ofs(Offset::y(ScreenBorder::WIDTH / 2)),
|
||||||
|
Offset::new(width, ScreenBorder::WIDTH),
|
||||||
|
);
|
||||||
|
(SCREEN, top_gap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation complete
|
||||||
|
_ => (SCREEN, TOP_GAP_ZERO),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shape::Circle::new(center, constant::LOADER_INNER + 2)
|
fn render_clipped_border<'s>(
|
||||||
.with_bg(background_color)
|
border: &'static ScreenBorder,
|
||||||
|
clip: Rect,
|
||||||
|
top_gap: Rect,
|
||||||
|
alpha: u8,
|
||||||
|
target: &mut impl Renderer<'s>,
|
||||||
|
) {
|
||||||
|
target.in_clip(clip, &|target| {
|
||||||
|
border.render(alpha, target);
|
||||||
|
});
|
||||||
|
shape::Bar::new(top_gap)
|
||||||
|
.with_bg(BG)
|
||||||
|
.with_fg(BG)
|
||||||
.render(target);
|
.render(target);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
mod loader;
|
mod loader;
|
||||||
mod screen_border;
|
mod screen_border;
|
||||||
|
|
||||||
pub use loader::{render_loader, LoaderRange};
|
pub use loader::render_loader;
|
||||||
pub use screen_border::ScreenBorder;
|
pub use screen_border::ScreenBorder;
|
||||||
|
@ -17,7 +17,7 @@ pub struct ScreenBorder {
|
|||||||
|
|
||||||
impl ScreenBorder {
|
impl ScreenBorder {
|
||||||
pub const WIDTH: i16 = 4;
|
pub const WIDTH: i16 = 4;
|
||||||
pub fn new(color: Color) -> Self {
|
pub const fn new(color: Color) -> Self {
|
||||||
let screen = constant::screen();
|
let screen = constant::screen();
|
||||||
|
|
||||||
// Top bar: from the right edge of top-left icon to the left edge of top-right
|
// Top bar: from the right edge of top-left icon to the left edge of top-right
|
||||||
|
Loading…
Reference in New Issue
Block a user