mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-03 16:56:07 +00:00
refactor(core/rust): introduce icon/image type
[no changelog]
This commit is contained in:
parent
461f566777
commit
236396338c
@ -288,7 +288,6 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_function("display_bar")
|
.allowlist_function("display_bar")
|
||||||
.allowlist_function("display_bar_radius")
|
.allowlist_function("display_bar_radius")
|
||||||
.allowlist_function("display_bar_radius_buffer")
|
.allowlist_function("display_bar_radius_buffer")
|
||||||
.allowlist_function("display_icon")
|
|
||||||
.allowlist_function("display_image")
|
.allowlist_function("display_image")
|
||||||
.allowlist_function("display_toif_info")
|
.allowlist_function("display_toif_info")
|
||||||
.allowlist_function("display_loader")
|
.allowlist_function("display_loader")
|
||||||
@ -321,8 +320,10 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_function("hal_delay")
|
.allowlist_function("hal_delay")
|
||||||
.allowlist_function("hal_ticks_ms")
|
.allowlist_function("hal_ticks_ms")
|
||||||
// dma2d
|
// dma2d
|
||||||
|
.allowlist_function("dma2d_setup_4bpp")
|
||||||
.allowlist_function("dma2d_setup_4bpp_over_4bpp")
|
.allowlist_function("dma2d_setup_4bpp_over_4bpp")
|
||||||
.allowlist_function("dma2d_setup_4bpp_over_16bpp")
|
.allowlist_function("dma2d_setup_4bpp_over_16bpp")
|
||||||
|
.allowlist_function("dma2d_start")
|
||||||
.allowlist_function("dma2d_start_blend")
|
.allowlist_function("dma2d_start_blend")
|
||||||
.allowlist_function("dma2d_wait_for_transfer")
|
.allowlist_function("dma2d_wait_for_transfer")
|
||||||
//buffers
|
//buffers
|
||||||
|
@ -5,7 +5,7 @@ use num_traits::FromPrimitive;
|
|||||||
|
|
||||||
use crate::trezorhal::buffers::BufferText;
|
use crate::trezorhal::buffers::BufferText;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Eq, FromPrimitive)]
|
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
|
||||||
pub enum ToifFormat {
|
pub enum ToifFormat {
|
||||||
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _,
|
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE as _,
|
||||||
GrayScaleOH = ffi::toif_format_t_TOIF_GRAYSCALE_OH as _,
|
GrayScaleOH = ffi::toif_format_t_TOIF_GRAYSCALE_OH as _,
|
||||||
@ -103,21 +103,6 @@ pub fn bar_radius_buffer(x: i16, y: i16, w: i16, h: i16, radius: u8, buffer: &mu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn icon(x: i16, y: i16, w: i16, h: i16, data: &[u8], fgcolor: u16, bgcolor: u16) {
|
|
||||||
unsafe {
|
|
||||||
ffi::display_icon(
|
|
||||||
x.into(),
|
|
||||||
y.into(),
|
|
||||||
w.into(),
|
|
||||||
h.into(),
|
|
||||||
data.as_ptr() as _,
|
|
||||||
data.len() as _,
|
|
||||||
fgcolor,
|
|
||||||
bgcolor,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn image(x: i16, y: i16, w: i16, h: i16, data: &[u8]) {
|
pub fn image(x: i16, y: i16, w: i16, h: i16, data: &[u8]) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::display_image(
|
ffi::display_image(
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
use super::ffi;
|
use super::ffi;
|
||||||
|
|
||||||
|
pub fn dma2d_setup_4bpp(fg_color: u16, bg_color: u16) {
|
||||||
|
unsafe { ffi::dma2d_setup_4bpp(fg_color, bg_color) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dma2d_setup_4bpp_over_4bpp(fg_color: u16, bg_color: u16, overlay_color: u16) {
|
pub fn dma2d_setup_4bpp_over_4bpp(fg_color: u16, bg_color: u16, overlay_color: u16) {
|
||||||
unsafe { ffi::dma2d_setup_4bpp_over_4bpp(fg_color, bg_color, overlay_color) }
|
unsafe { ffi::dma2d_setup_4bpp_over_4bpp(fg_color, bg_color, overlay_color) }
|
||||||
}
|
}
|
||||||
@ -8,6 +12,16 @@ pub fn dma2d_setup_4bpp_over_16bpp(overlay_color: u16) {
|
|||||||
unsafe { ffi::dma2d_setup_4bpp_over_16bpp(overlay_color) }
|
unsafe { ffi::dma2d_setup_4bpp_over_16bpp(overlay_color) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dma2d_start(buffer: &[u8], pixels: i16) {
|
||||||
|
unsafe {
|
||||||
|
ffi::dma2d_start(
|
||||||
|
buffer.as_ptr() as _,
|
||||||
|
ffi::DISPLAY_DATA_ADDRESS as _,
|
||||||
|
pixels as _,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels: i16) {
|
pub fn dma2d_start_blend(overlay_buffer: &[u8], bg_buffer: &[u8], pixels: i16) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ffi::dma2d_start_blend(
|
ffi::dma2d_start_blend(
|
||||||
|
@ -1,22 +1,38 @@
|
|||||||
use crate::ui::{
|
use crate::{
|
||||||
|
trezorhal::display::{image, ToifFormat},
|
||||||
|
ui::{
|
||||||
component::{Component, Event, EventCtx, Never},
|
component::{Component, Event, EventCtx, Never},
|
||||||
display,
|
display,
|
||||||
display::{toif_info, Color},
|
display::{
|
||||||
geometry::{Alignment, Offset, Point, Rect},
|
toif::{NamedToif, Toif},
|
||||||
|
Color, Icon,
|
||||||
|
},
|
||||||
|
geometry::{Alignment2D, Offset, Point, Rect, CENTER},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct Image {
|
pub struct Image {
|
||||||
image: &'static [u8],
|
pub toif: Toif,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn new(image: &'static [u8]) -> Self {
|
pub fn new(named_toif: NamedToif) -> Self {
|
||||||
|
let toif = Toif::new(named_toif);
|
||||||
|
ensure!(toif.format == ToifFormat::FullColorLE, toif.name);
|
||||||
Self {
|
Self {
|
||||||
image,
|
toif,
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Display the icon with baseline Point, aligned according to the
|
||||||
|
/// `alignment` argument.
|
||||||
|
pub fn draw(&self, baseline: Point, alignment: Alignment2D) {
|
||||||
|
let r = Rect::snap(baseline, self.toif.size, alignment);
|
||||||
|
image(r.x0, r.y0, r.width(), r.height(), self.toif.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Image {
|
impl Component for Image {
|
||||||
@ -32,13 +48,14 @@ impl Component for Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
display::image(self.area.center(), self.image)
|
self.draw(self.area.center(), CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
if let Some((size, _)) = display::toif_info(self.image) {
|
sink(Rect::from_center_and_size(
|
||||||
sink(Rect::from_center_and_size(self.area.center(), size));
|
self.area.center(),
|
||||||
}
|
self.toif.size,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +68,8 @@ impl crate::trace::Trace for Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlendedImage {
|
pub struct BlendedImage {
|
||||||
bg: &'static [u8],
|
bg: Icon,
|
||||||
fg: &'static [u8],
|
fg: Icon,
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
fg_color: Color,
|
fg_color: Color,
|
||||||
area_color: Color,
|
area_color: Color,
|
||||||
@ -61,13 +78,7 @@ pub struct BlendedImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlendedImage {
|
impl BlendedImage {
|
||||||
pub fn new(
|
pub fn new(bg: Icon, fg: Icon, bg_color: Color, fg_color: Color, area_color: Color) -> Self {
|
||||||
bg: &'static [u8],
|
|
||||||
fg: &'static [u8],
|
|
||||||
bg_color: Color,
|
|
||||||
fg_color: Color,
|
|
||||||
area_color: Color,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
bg,
|
bg,
|
||||||
fg,
|
fg,
|
||||||
@ -93,15 +104,12 @@ impl Component for BlendedImage {
|
|||||||
type Msg = Never;
|
type Msg = Never;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
let (bg_size, _) = unwrap!(toif_info(self.bg));
|
self.bg_top_left = self.bg.toif.size.snap(bounds.center(), CENTER);
|
||||||
|
|
||||||
self.bg_top_left = bg_size.snap(bounds.center(), Alignment::Center, Alignment::Center);
|
let ft_top_left = self.fg.toif.size.snap(bounds.center(), CENTER);
|
||||||
|
|
||||||
if let Some((fg_size, _)) = toif_info(self.fg) {
|
|
||||||
let ft_top_left = fg_size.snap(bounds.center(), Alignment::Center, Alignment::Center);
|
|
||||||
self.fg_offset = ft_top_left - self.bg_top_left;
|
self.fg_offset = ft_top_left - self.bg_top_left;
|
||||||
}
|
|
||||||
Rect::from_top_left_and_size(self.bg_top_left, bg_size)
|
Rect::from_top_left_and_size(self.bg_top_left, self.bg.toif.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||||
@ -113,9 +121,10 @@ impl Component for BlendedImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||||
if let Some((size, _)) = display::toif_info(self.bg) {
|
sink(Rect::from_top_left_and_size(
|
||||||
sink(Rect::from_top_left_and_size(self.bg_top_left, size));
|
self.bg_top_left,
|
||||||
}
|
self.bg.toif.size,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ pub mod timeout;
|
|||||||
pub use base::{Child, Component, ComponentExt, Event, EventCtx, Never, TimerToken};
|
pub use base::{Child, Component, ComponentExt, Event, EventCtx, Never, TimerToken};
|
||||||
pub use border::Border;
|
pub use border::Border;
|
||||||
pub use empty::Empty;
|
pub use empty::Empty;
|
||||||
pub use image::Image;
|
|
||||||
pub use label::Label;
|
pub use label::Label;
|
||||||
pub use map::Map;
|
pub use map::Map;
|
||||||
pub use maybe::Maybe;
|
pub use maybe::Maybe;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#[cfg(feature = "jpeg")]
|
#[cfg(feature = "jpeg")]
|
||||||
use crate::ui::geometry::Offset;
|
use crate::ui::geometry::Offset;
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Component, Event, EventCtx, Never},
|
component::{image::Image, Component, Event, EventCtx, Never},
|
||||||
display,
|
display,
|
||||||
geometry::Rect,
|
geometry::{Rect, CENTER},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Painter<F> {
|
pub struct Painter<F> {
|
||||||
@ -62,8 +62,8 @@ where
|
|||||||
Painter::new(f)
|
Painter::new(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn image_painter(image: &'static [u8]) -> Painter<impl FnMut(Rect)> {
|
pub fn image_painter(image: Image) -> Painter<impl FnMut(Rect)> {
|
||||||
let f = move |area: Rect| display::image(area.center(), image);
|
let f = move |area: Rect| image.draw(area.center(), CENTER);
|
||||||
Painter::new(f)
|
Painter::new(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ use heapless::Vec;
|
|||||||
|
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Component, Event, EventCtx, Never, Paginate},
|
component::{Component, Event, EventCtx, Never, Paginate},
|
||||||
display,
|
display::toif::Icon,
|
||||||
geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect},
|
geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect, TOP_LEFT},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::layout::{LayoutFit, TextLayout, TextStyle};
|
use super::layout::{LayoutFit, TextLayout, TextStyle};
|
||||||
@ -505,8 +505,8 @@ pub struct Checklist<T> {
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
paragraphs: Paragraphs<T>,
|
paragraphs: Paragraphs<T>,
|
||||||
current: usize,
|
current: usize,
|
||||||
icon_current: &'static [u8],
|
icon_current: Icon,
|
||||||
icon_done: &'static [u8],
|
icon_done: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Checklist<T> {
|
impl<T> Checklist<T> {
|
||||||
@ -515,8 +515,8 @@ impl<T> Checklist<T> {
|
|||||||
const CURRENT_OFFSET: Offset = Offset::new(2, 3);
|
const CURRENT_OFFSET: Offset = Offset::new(2, 3);
|
||||||
|
|
||||||
pub fn from_paragraphs(
|
pub fn from_paragraphs(
|
||||||
icon_current: &'static [u8],
|
icon_current: Icon,
|
||||||
icon_done: &'static [u8],
|
icon_done: Icon,
|
||||||
current: usize,
|
current: usize,
|
||||||
paragraphs: Paragraphs<T>,
|
paragraphs: Paragraphs<T>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -529,11 +529,11 @@ impl<T> Checklist<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_icon(&self, layout: &TextLayout, icon: &'static [u8], offset: Offset) {
|
fn paint_icon(&self, layout: &TextLayout, icon: Icon, offset: Offset) {
|
||||||
let top_left = Point::new(self.area.x0, layout.bounds.y0);
|
let top_left = Point::new(self.area.x0, layout.bounds.y0);
|
||||||
display::icon_top_left(
|
icon.draw(
|
||||||
top_left + offset,
|
top_left + offset,
|
||||||
icon,
|
TOP_LEFT,
|
||||||
layout.style.text_color,
|
layout.style.text_color,
|
||||||
layout.style.background_color,
|
layout.style.background_color,
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use crate::{
|
use crate::ui::{
|
||||||
trezorhal::uzlib::UzlibContext,
|
|
||||||
ui::{
|
|
||||||
constant, display,
|
constant, display,
|
||||||
display::{Color, ToifFormat},
|
display::Color,
|
||||||
geometry::{Offset, Point, Rect},
|
geometry::{Offset, Point, Rect},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use core::slice::from_raw_parts;
|
use core::slice::from_raw_parts;
|
||||||
|
|
||||||
@ -16,7 +13,7 @@ use crate::trezorhal::{
|
|||||||
|
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
constant::{screen, LOADER_OUTER},
|
constant::{screen, LOADER_OUTER},
|
||||||
display::toif_info_ensure,
|
display::toif::{Icon, NamedToif},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const LOADER_MIN: u16 = 0;
|
pub const LOADER_MIN: u16 = 0;
|
||||||
@ -41,17 +38,15 @@ pub fn loader_uncompress(
|
|||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
progress: u16,
|
progress: u16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
icon: Option<(&[u8], Color)>,
|
icon: Option<(Icon, Color)>,
|
||||||
) {
|
) {
|
||||||
const ICON_MAX_SIZE: i16 = constant::LOADER_ICON_MAX_SIZE;
|
const ICON_MAX_SIZE: i16 = constant::LOADER_ICON_MAX_SIZE;
|
||||||
|
|
||||||
if let Some((data, color)) = icon {
|
if let Some((icon, color)) = icon {
|
||||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH);
|
if icon.toif.width() <= ICON_MAX_SIZE && icon.toif.height() <= ICON_MAX_SIZE {
|
||||||
if toif_size.x <= ICON_MAX_SIZE && toif_size.y <= ICON_MAX_SIZE {
|
|
||||||
let mut icon_data = [0_u8; ((ICON_MAX_SIZE * ICON_MAX_SIZE) / 2) as usize];
|
let mut icon_data = [0_u8; ((ICON_MAX_SIZE * ICON_MAX_SIZE) / 2) as usize];
|
||||||
let mut ctx = UzlibContext::new(toif_data, None);
|
icon.toif.uncompress(&mut icon_data);
|
||||||
unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed");
|
let i = Some((icon, color, icon.toif.size));
|
||||||
let i = Some((icon_data.as_ref(), color, toif_size));
|
|
||||||
loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, i);
|
loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, i);
|
||||||
} else {
|
} else {
|
||||||
loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, None);
|
loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, None);
|
||||||
@ -78,7 +73,7 @@ pub extern "C" fn loader_uncompress_r(
|
|||||||
|
|
||||||
let i = if icon_data != 0 {
|
let i = if icon_data != 0 {
|
||||||
let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) };
|
let data_slice = unsafe { from_raw_parts(icon_data as _, icon_data_size as _) };
|
||||||
Some((data_slice, ic_color))
|
Some((Icon::new(NamedToif(data_slice, "loader icon")), ic_color))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -193,7 +188,7 @@ pub fn loader_rust(
|
|||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
progress: u16,
|
progress: u16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
icon: Option<(&[u8], Color, Offset)>,
|
icon: Option<(Icon, Color, Offset)>,
|
||||||
) {
|
) {
|
||||||
let center = screen().center() + Offset::new(0, y_offset);
|
let center = screen().center() + Offset::new(0, y_offset);
|
||||||
let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2));
|
let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2));
|
||||||
@ -209,14 +204,14 @@ pub fn loader_rust(
|
|||||||
let mut icon_area = Rect::zero();
|
let mut icon_area = Rect::zero();
|
||||||
let mut icon_area_clamped = Rect::zero();
|
let mut icon_area_clamped = Rect::zero();
|
||||||
let mut icon_width = 0;
|
let mut icon_width = 0;
|
||||||
let mut icon_data = [].as_ref();
|
let mut icon_data = None;
|
||||||
|
|
||||||
if let Some((data, color, size)) = icon {
|
if let Some((data, color, size)) = icon {
|
||||||
if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE {
|
if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE {
|
||||||
icon_width = size.x;
|
icon_width = size.x;
|
||||||
icon_area = Rect::from_center_and_size(center, size);
|
icon_area = Rect::from_center_and_size(center, size);
|
||||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||||
icon_data = data;
|
icon_data = Some(data);
|
||||||
use_icon = true;
|
use_icon = true;
|
||||||
icon_colortable = display::get_color_table(color, bg_color);
|
icon_colortable = display::get_color_table(color, bg_color);
|
||||||
}
|
}
|
||||||
@ -242,7 +237,8 @@ pub fn loader_rust(
|
|||||||
let x_i = x_c - icon_area.x0;
|
let x_i = x_c - icon_area.x0;
|
||||||
let y_i = y_c - icon_area.y0;
|
let y_i = y_c - icon_area.y0;
|
||||||
|
|
||||||
let data = icon_data[(((x_i & 0xFE) + (y_i * icon_width)) / 2) as usize];
|
let data = unwrap!(icon_data).data()
|
||||||
|
[(((x_i & 0xFE) + (y_i * icon_width)) / 2) as usize];
|
||||||
if (x_i & 0x01) == 0 {
|
if (x_i & 0x01) == 0 {
|
||||||
underlying_color = icon_colortable[(data & 0xF) as usize];
|
underlying_color = icon_colortable[(data & 0xF) as usize];
|
||||||
} else {
|
} else {
|
||||||
@ -273,7 +269,7 @@ pub fn loader_rust(
|
|||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
progress: u16,
|
progress: u16,
|
||||||
indeterminate: bool,
|
indeterminate: bool,
|
||||||
icon: Option<(&[u8], Color, Offset)>,
|
icon: Option<(Icon, Color, Offset)>,
|
||||||
) {
|
) {
|
||||||
let center = screen().center() + Offset::new(0, y_offset);
|
let center = screen().center() + Offset::new(0, y_offset);
|
||||||
let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2));
|
let r = Rect::from_center_and_size(center, Offset::uniform(LOADER_OUTER as i16 * 2));
|
||||||
@ -288,16 +284,16 @@ pub fn loader_rust(
|
|||||||
let mut icon_width = 0;
|
let mut icon_width = 0;
|
||||||
let mut icon_offset = 0;
|
let mut icon_offset = 0;
|
||||||
let mut icon_color = Color::from_u16(0);
|
let mut icon_color = Color::from_u16(0);
|
||||||
let mut icon_data = [].as_ref();
|
let mut icon_data = None;
|
||||||
|
|
||||||
if let Some((data, color, size)) = icon {
|
if let Some((icon, color, size)) = icon {
|
||||||
if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE {
|
if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE {
|
||||||
icon_width = size.x;
|
icon_width = size.x;
|
||||||
icon_area = Rect::from_center_and_size(center, size);
|
icon_area = Rect::from_center_and_size(center, size);
|
||||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||||
icon_offset = (icon_area_clamped.x0 - r.x0) / 2;
|
icon_offset = (icon_area_clamped.x0 - r.x0) / 2;
|
||||||
icon_color = color;
|
icon_color = color;
|
||||||
icon_data = data;
|
icon_data = Some(icon);
|
||||||
use_icon = true;
|
use_icon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,7 +337,7 @@ pub fn loader_rust(
|
|||||||
|
|
||||||
icon_buffer_used.buffer[icon_offset as usize..(icon_offset + icon_width / 2) as usize]
|
icon_buffer_used.buffer[icon_offset as usize..(icon_offset + icon_width / 2) as usize]
|
||||||
.copy_from_slice(
|
.copy_from_slice(
|
||||||
&icon_data[(y_i * (icon_width / 2)) as usize
|
&unwrap!(icon_data).toif.data[(y_i * (icon_width / 2)) as usize
|
||||||
..((y_i + 1) * (icon_width / 2)) as usize],
|
..((y_i + 1) * (icon_width / 2)) as usize],
|
||||||
);
|
);
|
||||||
icon_buffer = icon_buffer_used;
|
icon_buffer = icon_buffer_used;
|
||||||
@ -380,7 +376,7 @@ pub fn loader(
|
|||||||
y_offset: i16,
|
y_offset: i16,
|
||||||
fg_color: Color,
|
fg_color: Color,
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
icon: Option<(&[u8], Color)>,
|
icon: Option<(Icon, Color)>,
|
||||||
) {
|
) {
|
||||||
loader_uncompress(y_offset, fg_color, bg_color, progress, false, icon);
|
loader_uncompress(y_offset, fg_color, bg_color, progress, false, icon);
|
||||||
}
|
}
|
||||||
@ -390,7 +386,7 @@ pub fn loader_indeterminate(
|
|||||||
y_offset: i16,
|
y_offset: i16,
|
||||||
fg_color: Color,
|
fg_color: Color,
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
icon: Option<(&[u8], Color)>,
|
icon: Option<(Icon, Color)>,
|
||||||
) {
|
) {
|
||||||
loader_uncompress(y_offset, fg_color, bg_color, progress, true, icon);
|
loader_uncompress(y_offset, fg_color, bg_color, progress, true, icon);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
pub mod loader;
|
pub mod loader;
|
||||||
#[cfg(feature = "jpeg")]
|
#[cfg(feature = "jpeg")]
|
||||||
pub mod tjpgd;
|
pub mod tjpgd;
|
||||||
|
pub mod toif;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
constant,
|
constant,
|
||||||
@ -14,20 +15,21 @@ use crate::trezorhal::{
|
|||||||
dma2d_setup_4bpp_over_16bpp, dma2d_setup_4bpp_over_4bpp, dma2d_start_blend,
|
dma2d_setup_4bpp_over_16bpp, dma2d_setup_4bpp_over_4bpp, dma2d_start_blend,
|
||||||
dma2d_wait_for_transfer,
|
dma2d_wait_for_transfer,
|
||||||
},
|
},
|
||||||
|
uzlib::UZLIB_WINDOW_SIZE,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(feature = "dma2d"))]
|
||||||
|
use crate::ui::geometry::TOP_LEFT;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
trezorhal::{
|
trezorhal::{display, qr, time, uzlib::UzlibContext},
|
||||||
display,
|
|
||||||
display::ToifFormat,
|
|
||||||
qr, time,
|
|
||||||
uzlib::{UzlibContext, UZLIB_WINDOW_SIZE},
|
|
||||||
},
|
|
||||||
ui::lerp::Lerp,
|
ui::lerp::Lerp,
|
||||||
};
|
};
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
|
use crate::ui::component::image::Image;
|
||||||
|
pub use crate::ui::display::toif::Icon;
|
||||||
#[cfg(any(feature = "model_tt", feature = "model_tr"))]
|
#[cfg(any(feature = "model_tt", feature = "model_tr"))]
|
||||||
pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN};
|
pub use loader::{loader, loader_indeterminate, LOADER_MAX, LOADER_MIN};
|
||||||
|
|
||||||
@ -81,106 +83,6 @@ pub fn rect_fill_rounded(r: Rect, fg_color: Color, bg_color: Color, radius: u8)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE: Cannot start at odd x-coordinate. In this case icon is shifted 1px
|
|
||||||
/// left.
|
|
||||||
pub fn icon_top_left(top_left: Point, data: &[u8], fg_color: Color, bg_color: Color) {
|
|
||||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH);
|
|
||||||
display::icon(
|
|
||||||
top_left.x,
|
|
||||||
top_left.y,
|
|
||||||
toif_size.x,
|
|
||||||
toif_size.y,
|
|
||||||
toif_data,
|
|
||||||
fg_color.into(),
|
|
||||||
bg_color.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon(center: Point, data: &[u8], fg_color: Color, bg_color: Color) {
|
|
||||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH);
|
|
||||||
let r = Rect::from_center_and_size(center, toif_size);
|
|
||||||
display::icon(
|
|
||||||
r.x0,
|
|
||||||
r.y0,
|
|
||||||
r.width(),
|
|
||||||
r.height(),
|
|
||||||
toif_data,
|
|
||||||
fg_color.into(),
|
|
||||||
bg_color.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn icon_rust(center: Point, data: &[u8], fg_color: Color, bg_color: Color) {
|
|
||||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH);
|
|
||||||
let r = Rect::from_center_and_size(center, toif_size);
|
|
||||||
|
|
||||||
let area = r.translate(get_offset());
|
|
||||||
let clamped = area.clamp(constant::screen());
|
|
||||||
let colortable = get_color_table(fg_color, bg_color);
|
|
||||||
|
|
||||||
set_window(clamped);
|
|
||||||
|
|
||||||
let mut dest = [0_u8; 1];
|
|
||||||
|
|
||||||
let mut window = [0; UZLIB_WINDOW_SIZE];
|
|
||||||
let mut ctx = UzlibContext::new(toif_data, Some(&mut window));
|
|
||||||
|
|
||||||
for py in area.y0..area.y1 {
|
|
||||||
for px in area.x0..area.x1 {
|
|
||||||
let p = Point::new(px, py);
|
|
||||||
let x = p.x - area.x0;
|
|
||||||
|
|
||||||
if clamped.contains(p) {
|
|
||||||
if x % 2 == 0 {
|
|
||||||
unwrap!(ctx.uncompress(&mut dest), "Decompression failed");
|
|
||||||
pixeldata(colortable[(dest[0] & 0xF) as usize]);
|
|
||||||
} else {
|
|
||||||
pixeldata(colortable[(dest[0] >> 4) as usize]);
|
|
||||||
}
|
|
||||||
} else if x % 2 == 0 {
|
|
||||||
//continue unzipping but dont write to display
|
|
||||||
unwrap!(ctx.uncompress(&mut dest), "Decompression failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pixeldata_dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn image(center: Point, data: &[u8]) {
|
|
||||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::FullColorLE);
|
|
||||||
|
|
||||||
let r = Rect::from_center_and_size(center, toif_size);
|
|
||||||
display::image(r.x0, r.y0, r.width(), r.height(), toif_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toif_info(data: &[u8]) -> Option<(Offset, ToifFormat)> {
|
|
||||||
if let Ok(info) = display::toif_info(data) {
|
|
||||||
Some((
|
|
||||||
Offset::new(
|
|
||||||
unwrap!(info.width.try_into()),
|
|
||||||
unwrap!(info.height.try_into()),
|
|
||||||
),
|
|
||||||
info.format,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Aborts if the TOIF file does not have the correct grayscale flag, do not use
|
|
||||||
/// with user-supplied inputs.
|
|
||||||
pub(crate) fn toif_info_ensure(data: &[u8], format: ToifFormat) -> (Offset, &[u8]) {
|
|
||||||
let info = unwrap!(display::toif_info(data), "Invalid TOIF data");
|
|
||||||
assert_eq!(info.format, format);
|
|
||||||
let size = Offset::new(
|
|
||||||
unwrap!(info.width.try_into()),
|
|
||||||
unwrap!(info.height.try_into()),
|
|
||||||
);
|
|
||||||
let payload = &data[12..]; // Skip TOIF header.
|
|
||||||
(size, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used on T1 only.
|
// Used on T1 only.
|
||||||
pub fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) {
|
pub fn rect_fill_rounded1(r: Rect, fg_color: Color, bg_color: Color) {
|
||||||
display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into());
|
display::bar(r.x0, r.y0, r.width(), r.height(), fg_color.into());
|
||||||
@ -306,7 +208,7 @@ pub fn rect_rounded2_partial(
|
|||||||
fg_color: Color,
|
fg_color: Color,
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
show_percent: i16,
|
show_percent: i16,
|
||||||
icon: Option<(&[u8], Color)>,
|
icon: Option<(Icon, Color)>,
|
||||||
) {
|
) {
|
||||||
const MAX_ICON_SIZE: i16 = 64;
|
const MAX_ICON_SIZE: i16 = 64;
|
||||||
|
|
||||||
@ -325,17 +227,13 @@ pub fn rect_rounded2_partial(
|
|||||||
let mut icon_data = [0_u8; ((MAX_ICON_SIZE * MAX_ICON_SIZE) / 2) as usize];
|
let mut icon_data = [0_u8; ((MAX_ICON_SIZE * MAX_ICON_SIZE) / 2) as usize];
|
||||||
let mut icon_width = 0;
|
let mut icon_width = 0;
|
||||||
|
|
||||||
if let Some((icon_bytes, icon_color)) = icon {
|
if let Some((icon, icon_color)) = icon {
|
||||||
let (toif_size, toif_data) = toif_info_ensure(icon_bytes, ToifFormat::GrayScaleEH);
|
if icon.toif.width() <= MAX_ICON_SIZE && icon.toif.height() <= MAX_ICON_SIZE {
|
||||||
|
icon_area = Rect::from_center_and_size(center, icon.toif.size);
|
||||||
if toif_size.x <= MAX_ICON_SIZE && toif_size.y <= MAX_ICON_SIZE {
|
|
||||||
icon_area = Rect::from_center_and_size(center, toif_size);
|
|
||||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||||
|
icon.toif.uncompress(&mut icon_data);
|
||||||
let mut ctx = UzlibContext::new(toif_data, None);
|
|
||||||
unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed");
|
|
||||||
icon_colortable = get_color_table(icon_color, bg_color);
|
icon_colortable = get_color_table(icon_color, bg_color);
|
||||||
icon_width = toif_size.x;
|
icon_width = icon.toif.width();
|
||||||
use_icon = true;
|
use_icon = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,7 +431,7 @@ fn process_buffer(
|
|||||||
#[cfg(feature = "dma2d")]
|
#[cfg(feature = "dma2d")]
|
||||||
pub fn text_over_image(
|
pub fn text_over_image(
|
||||||
bg_area: Option<(Rect, Color)>,
|
bg_area: Option<(Rect, Color)>,
|
||||||
image_data: &[u8],
|
image: Image,
|
||||||
text: &str,
|
text: &str,
|
||||||
font: Font,
|
font: Font,
|
||||||
offset_img: Offset,
|
offset_img: Offset,
|
||||||
@ -548,8 +446,6 @@ pub fn text_over_image(
|
|||||||
let t2 = unsafe { get_buffer_4bpp(1, true) };
|
let t2 = unsafe { get_buffer_4bpp(1, true) };
|
||||||
let empty_t = unsafe { get_buffer_4bpp(2, true) };
|
let empty_t = unsafe { get_buffer_4bpp(2, true) };
|
||||||
|
|
||||||
let (toif_size, toif_data) = toif_info_ensure(image_data, ToifFormat::FullColorLE);
|
|
||||||
|
|
||||||
let r_img;
|
let r_img;
|
||||||
let area;
|
let area;
|
||||||
let offset_img_final;
|
let offset_img_final;
|
||||||
@ -565,10 +461,10 @@ pub fn text_over_image(
|
|||||||
empty_img.buffer.copy_from_slice(&img1.buffer);
|
empty_img.buffer.copy_from_slice(&img1.buffer);
|
||||||
|
|
||||||
area = a;
|
area = a;
|
||||||
r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, toif_size);
|
r_img = Rect::from_top_left_and_size(a.top_left() + offset_img, image.toif.size);
|
||||||
offset_img_final = offset_img;
|
offset_img_final = offset_img;
|
||||||
} else {
|
} else {
|
||||||
area = Rect::from_top_left_and_size(offset_img.into(), toif_size);
|
area = Rect::from_top_left_and_size(offset_img.into(), image.toif.size);
|
||||||
r_img = area;
|
r_img = area;
|
||||||
offset_img_final = Offset::zero();
|
offset_img_final = Offset::zero();
|
||||||
}
|
}
|
||||||
@ -594,7 +490,7 @@ pub fn text_over_image(
|
|||||||
set_window(clamped);
|
set_window(clamped);
|
||||||
|
|
||||||
let mut window = [0; UZLIB_WINDOW_SIZE];
|
let mut window = [0; UZLIB_WINDOW_SIZE];
|
||||||
let mut ctx = UzlibContext::new(toif_data, Some(&mut window));
|
let mut ctx = image.toif.decompression_context(Some(&mut window));
|
||||||
|
|
||||||
dma2d_setup_4bpp_over_16bpp(text_color.into());
|
dma2d_setup_4bpp_over_16bpp(text_color.into());
|
||||||
|
|
||||||
@ -662,8 +558,8 @@ pub fn text_over_image(
|
|||||||
#[cfg(feature = "dma2d")]
|
#[cfg(feature = "dma2d")]
|
||||||
pub fn icon_over_icon(
|
pub fn icon_over_icon(
|
||||||
bg_area: Option<Rect>,
|
bg_area: Option<Rect>,
|
||||||
bg: (&[u8], Offset, Color),
|
bg: (Icon, Offset, Color),
|
||||||
fg: (&[u8], Offset, Color),
|
fg: (Icon, Offset, Color),
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
) {
|
) {
|
||||||
let bg1 = unsafe { get_buffer_16bpp(0, true) };
|
let bg1 = unsafe { get_buffer_16bpp(0, true) };
|
||||||
@ -673,41 +569,40 @@ pub fn icon_over_icon(
|
|||||||
let fg2 = unsafe { get_buffer_4bpp(1, true) };
|
let fg2 = unsafe { get_buffer_4bpp(1, true) };
|
||||||
let empty2 = unsafe { get_buffer_4bpp(2, true) };
|
let empty2 = unsafe { get_buffer_4bpp(2, true) };
|
||||||
|
|
||||||
let (data_bg, offset_bg, color_icon_bg) = bg;
|
let (icon_bg, offset_bg, color_icon_bg) = bg;
|
||||||
let (data_fg, offset_fg, color_icon_fg) = fg;
|
let (icon_fg, offset_fg, color_icon_fg) = fg;
|
||||||
|
|
||||||
let (toif_bg_size, toif_bg_data) = toif_info_ensure(data_bg, ToifFormat::GrayScaleEH);
|
assert!(icon_bg.toif.width() <= constant::WIDTH);
|
||||||
assert!(toif_bg_size.x <= constant::WIDTH);
|
assert_eq!(icon_bg.toif.width() % 2, 0);
|
||||||
assert_eq!(toif_bg_size.x % 2, 0);
|
|
||||||
|
|
||||||
let (toif_fg_size, toif_fg_data) = toif_info_ensure(data_fg, ToifFormat::GrayScaleEH);
|
assert!(icon_fg.toif.width() <= constant::WIDTH);
|
||||||
assert!(toif_bg_size.x <= constant::WIDTH);
|
assert_eq!(icon_fg.toif.width() % 2, 0);
|
||||||
assert_eq!(toif_bg_size.x % 2, 0);
|
|
||||||
|
|
||||||
let area;
|
let area;
|
||||||
let r_bg;
|
let r_bg;
|
||||||
let final_offset_bg;
|
let final_offset_bg;
|
||||||
if let Some(a) = bg_area {
|
if let Some(a) = bg_area {
|
||||||
area = a;
|
area = a;
|
||||||
r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, toif_bg_size);
|
r_bg = Rect::from_top_left_and_size(a.top_left() + offset_bg, icon_bg.toif.size);
|
||||||
final_offset_bg = offset_bg;
|
final_offset_bg = offset_bg;
|
||||||
} else {
|
} else {
|
||||||
r_bg = Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), toif_bg_size);
|
r_bg =
|
||||||
|
Rect::from_top_left_and_size(Point::new(offset_bg.x, offset_bg.y), icon_bg.toif.size);
|
||||||
area = r_bg;
|
area = r_bg;
|
||||||
final_offset_bg = Offset::zero();
|
final_offset_bg = Offset::zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, toif_fg_size);
|
let r_fg = Rect::from_top_left_and_size(area.top_left() + offset_fg, icon_fg.toif.size);
|
||||||
|
|
||||||
let clamped = area.clamp(constant::screen()).ensure_even_width();
|
let clamped = area.clamp(constant::screen()).ensure_even_width();
|
||||||
|
|
||||||
set_window(clamped);
|
set_window(clamped);
|
||||||
|
|
||||||
let mut window_bg = [0; UZLIB_WINDOW_SIZE];
|
let mut window_bg = [0; UZLIB_WINDOW_SIZE];
|
||||||
let mut ctx_bg = UzlibContext::new(toif_bg_data, Some(&mut window_bg));
|
let mut ctx_bg = UzlibContext::new(icon_bg.toif.data, Some(&mut window_bg));
|
||||||
|
|
||||||
let mut window_fg = [0; UZLIB_WINDOW_SIZE];
|
let mut window_fg = [0; UZLIB_WINDOW_SIZE];
|
||||||
let mut ctx_fg = UzlibContext::new(toif_fg_data, Some(&mut window_fg));
|
let mut ctx_fg = UzlibContext::new(icon_fg.toif.data, Some(&mut window_fg));
|
||||||
|
|
||||||
dma2d_setup_4bpp_over_4bpp(color_icon_bg.into(), bg_color.into(), color_icon_fg.into());
|
dma2d_setup_4bpp_over_4bpp(color_icon_bg.into(), bg_color.into(), color_icon_fg.into());
|
||||||
|
|
||||||
@ -766,12 +661,12 @@ pub fn icon_over_icon(
|
|||||||
#[cfg(not(feature = "dma2d"))]
|
#[cfg(not(feature = "dma2d"))]
|
||||||
pub fn icon_over_icon(
|
pub fn icon_over_icon(
|
||||||
bg_area: Option<Rect>,
|
bg_area: Option<Rect>,
|
||||||
bg: (&[u8], Offset, Color),
|
bg: (Icon, Offset, Color),
|
||||||
fg: (&[u8], Offset, Color),
|
fg: (Icon, Offset, Color),
|
||||||
bg_color: Color,
|
bg_color: Color,
|
||||||
) {
|
) {
|
||||||
let (data_bg, offset_bg, color_icon_bg) = bg;
|
let (icon_bg, offset_bg, color_icon_bg) = bg;
|
||||||
let (data_fg, offset_fg, color_icon_fg) = fg;
|
let (icon_fg, offset_fg, color_icon_fg) = fg;
|
||||||
|
|
||||||
let pos_bg = if let Some(area) = bg_area {
|
let pos_bg = if let Some(area) = bg_area {
|
||||||
rect_fill(area, bg_color);
|
rect_fill(area, bg_color);
|
||||||
@ -780,8 +675,8 @@ pub fn icon_over_icon(
|
|||||||
Point::from(offset_bg)
|
Point::from(offset_bg)
|
||||||
};
|
};
|
||||||
|
|
||||||
icon_top_left(pos_bg, data_bg, color_icon_bg, bg_color);
|
icon_bg.draw(pos_bg, TOP_LEFT, color_icon_bg, bg_color);
|
||||||
icon_top_left(pos_bg + offset_fg, data_fg, color_icon_fg, color_icon_bg);
|
icon_fg.draw(pos_bg + offset_fg, TOP_LEFT, color_icon_fg, color_icon_bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a color of a pixel on `p` coordinates of rounded rectangle with corner
|
/// Gets a color of a pixel on `p` coordinates of rounded rectangle with corner
|
||||||
|
131
core/embed/rust/src/ui/display/toif.rs
Normal file
131
core/embed/rust/src/ui/display/toif.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use crate::{
|
||||||
|
trezorhal,
|
||||||
|
trezorhal::{
|
||||||
|
display::ToifFormat,
|
||||||
|
uzlib::{UzlibContext, UZLIB_WINDOW_SIZE},
|
||||||
|
},
|
||||||
|
ui::{
|
||||||
|
constant,
|
||||||
|
display::{get_color_table, get_offset, pixeldata, pixeldata_dirty, set_window},
|
||||||
|
geometry::{Alignment2D, Offset, Point, Rect},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Color;
|
||||||
|
|
||||||
|
const TOIF_HEADER_LENGTH: usize = 12;
|
||||||
|
|
||||||
|
/// Storing the icon together with its name
|
||||||
|
/// Needs to be a tuple-struct, so it can be made `const`
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct NamedToif(pub &'static [u8], pub &'static str);
|
||||||
|
|
||||||
|
pub fn toif_info(data: &[u8]) -> Option<(Offset, ToifFormat)> {
|
||||||
|
if let Ok(info) = trezorhal::display::toif_info(data) {
|
||||||
|
Some((
|
||||||
|
Offset::new(
|
||||||
|
unwrap!(info.width.try_into()),
|
||||||
|
unwrap!(info.height.try_into()),
|
||||||
|
),
|
||||||
|
info.format,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn icon(icon: &Icon, center: Point, fg_color: Color, bg_color: Color) {
|
||||||
|
let r = Rect::from_center_and_size(center, icon.toif.size);
|
||||||
|
let area = r.translate(get_offset());
|
||||||
|
let clamped = area.clamp(constant::screen());
|
||||||
|
let colortable = get_color_table(fg_color, bg_color);
|
||||||
|
|
||||||
|
set_window(clamped);
|
||||||
|
|
||||||
|
let mut dest = [0_u8; 1];
|
||||||
|
|
||||||
|
let mut window = [0; UZLIB_WINDOW_SIZE];
|
||||||
|
let mut ctx = icon.toif.decompression_context(Some(&mut window));
|
||||||
|
|
||||||
|
for py in area.y0..area.y1 {
|
||||||
|
for px in area.x0..area.x1 {
|
||||||
|
let p = Point::new(px, py);
|
||||||
|
let x = p.x - area.x0;
|
||||||
|
|
||||||
|
if clamped.contains(p) {
|
||||||
|
if x % 2 == 0 {
|
||||||
|
unwrap!(ctx.uncompress(&mut dest), "Decompression failed");
|
||||||
|
pixeldata(colortable[(dest[0] & 0xF) as usize]);
|
||||||
|
} else {
|
||||||
|
pixeldata(colortable[(dest[0] >> 4) as usize]);
|
||||||
|
}
|
||||||
|
} else if x % 2 == 0 {
|
||||||
|
//continue unzipping but dont write to display
|
||||||
|
unwrap!(ctx.uncompress(&mut dest), "Decompression failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixeldata_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holding toif data and allowing it to draw itself.
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Toif {
|
||||||
|
pub data: &'static [u8],
|
||||||
|
pub name: &'static str, // useful for debugging purposes.
|
||||||
|
pub size: Offset,
|
||||||
|
pub format: ToifFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Toif {
|
||||||
|
pub fn new(named_toif: NamedToif) -> Self {
|
||||||
|
let info = unwrap!(toif_info(named_toif.0));
|
||||||
|
Self {
|
||||||
|
data: named_toif.0[TOIF_HEADER_LENGTH..].as_ref(),
|
||||||
|
name: named_toif.1,
|
||||||
|
size: info.0,
|
||||||
|
format: info.1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn width(&self) -> i16 {
|
||||||
|
self.size.x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> i16 {
|
||||||
|
self.size.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uncompress(&self, dest: &mut [u8]) {
|
||||||
|
let mut ctx = self.decompression_context(None);
|
||||||
|
unwrap!(ctx.uncompress(dest), self.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decompression_context<'a>(
|
||||||
|
&'a self,
|
||||||
|
window: Option<&'a mut [u8; UZLIB_WINDOW_SIZE]>,
|
||||||
|
) -> UzlibContext {
|
||||||
|
UzlibContext::new(self.data, window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct Icon {
|
||||||
|
pub toif: Toif,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Icon {
|
||||||
|
pub fn new(named_toif: NamedToif) -> Self {
|
||||||
|
let toif = Toif::new(named_toif);
|
||||||
|
ensure!(toif.format == ToifFormat::GrayScaleEH, toif.name);
|
||||||
|
Self { toif }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Display the icon with baseline Point, aligned according to the
|
||||||
|
/// `alignment` argument.
|
||||||
|
pub fn draw(&self, baseline: Point, alignment: Alignment2D, fg_color: Color, bg_color: Color) {
|
||||||
|
let r = Rect::snap(baseline, self.toif.size, alignment);
|
||||||
|
icon(self, r.center(), fg_color, bg_color);
|
||||||
|
}
|
||||||
|
}
|
@ -77,13 +77,13 @@ impl Offset {
|
|||||||
|
|
||||||
/// With `self` representing a rectangle size, returns top-left corner of
|
/// With `self` representing a rectangle size, returns top-left corner of
|
||||||
/// the rectangle such that it is aligned relative to the `point`.
|
/// the rectangle such that it is aligned relative to the `point`.
|
||||||
pub const fn snap(self, point: Point, x: Alignment, y: Alignment) -> Point {
|
pub const fn snap(self, point: Point, alignment: Alignment2D) -> Point {
|
||||||
let x_off = match x {
|
let x_off = match alignment.0 {
|
||||||
Alignment::Start => 0,
|
Alignment::Start => 0,
|
||||||
Alignment::Center => self.x / 2,
|
Alignment::Center => self.x / 2,
|
||||||
Alignment::End => self.x,
|
Alignment::End => self.x,
|
||||||
};
|
};
|
||||||
let y_off = match y {
|
let y_off = match alignment.1 {
|
||||||
Alignment::Start => 0,
|
Alignment::Start => 0,
|
||||||
Alignment::Center => self.y / 2,
|
Alignment::Center => self.y / 2,
|
||||||
Alignment::End => self.y,
|
Alignment::End => self.y,
|
||||||
@ -224,6 +224,12 @@ impl Rect {
|
|||||||
Self::new(Point::zero(), Point::zero())
|
Self::new(Point::zero(), Point::zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a rectangle of `size` such that `point` is on position specified
|
||||||
|
/// by `alignment`.
|
||||||
|
pub const fn snap(point: Point, size: Offset, alignment: Alignment2D) -> Rect {
|
||||||
|
Self::from_top_left_and_size(size.snap(point, alignment), size)
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn from_top_left_and_size(p0: Point, size: Offset) -> Self {
|
pub const fn from_top_left_and_size(p0: Point, size: Offset) -> Self {
|
||||||
Self {
|
Self {
|
||||||
x0: p0.x,
|
x0: p0.x,
|
||||||
@ -451,6 +457,14 @@ pub enum Alignment {
|
|||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Alignment2D = (Alignment, Alignment);
|
||||||
|
|
||||||
|
pub const TOP_LEFT: Alignment2D = (Alignment::Start, Alignment::Start);
|
||||||
|
pub const TOP_RIGHT: Alignment2D = (Alignment::Start, Alignment::End);
|
||||||
|
pub const CENTER: Alignment2D = (Alignment::Center, Alignment::Center);
|
||||||
|
pub const BOTTOM_LEFT: Alignment2D = (Alignment::Start, Alignment::End);
|
||||||
|
pub const BOTTOM_RIGHT: Alignment2D = (Alignment::End, Alignment::End);
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Axis {
|
pub enum Axis {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
|
@ -4,6 +4,7 @@ use crate::{
|
|||||||
animation::Animation,
|
animation::Animation,
|
||||||
component::{Component, Event, EventCtx},
|
component::{Component, Event, EventCtx},
|
||||||
display,
|
display,
|
||||||
|
display::toif::Icon,
|
||||||
geometry::Rect,
|
geometry::Rect,
|
||||||
model_tr::theme,
|
model_tr::theme,
|
||||||
},
|
},
|
||||||
@ -23,11 +24,11 @@ pub struct ResultAnim {
|
|||||||
area: Rect,
|
area: Rect,
|
||||||
state: State,
|
state: State,
|
||||||
growing_duration: Duration,
|
growing_duration: Duration,
|
||||||
icon: &'static [u8],
|
icon: Icon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResultAnim {
|
impl ResultAnim {
|
||||||
pub fn new(icon: &'static [u8]) -> Self {
|
pub fn new(icon: Icon) -> Self {
|
||||||
Self {
|
Self {
|
||||||
area: Rect::zero(),
|
area: Rect::zero(),
|
||||||
state: State::Initial,
|
state: State::Initial,
|
||||||
|
@ -6,6 +6,7 @@ use crate::{
|
|||||||
Child, Component, ComponentExt, Event, EventCtx, Label, Pad,
|
Child, Component, ComponentExt, Event, EventCtx, Label, Pad,
|
||||||
},
|
},
|
||||||
constant::screen,
|
constant::screen,
|
||||||
|
display::toif::Icon,
|
||||||
geometry::{Alignment, Insets, LinearPlacement, Point, Rect},
|
geometry::{Alignment, Insets, LinearPlacement, Point, Rect},
|
||||||
model_tr::{
|
model_tr::{
|
||||||
component::{Button, ButtonMsg, ButtonPos, ResultAnim, ResultAnimMsg},
|
component::{Button, ButtonMsg, ButtonPos, ResultAnim, ResultAnimMsg},
|
||||||
@ -38,7 +39,7 @@ const ANIM_POS_ADJ_BUTTON: i16 = 6;
|
|||||||
|
|
||||||
impl<S: ParagraphStrType> ResultPopup<S> {
|
impl<S: ParagraphStrType> ResultPopup<S> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
icon: &'static [u8],
|
icon: Icon,
|
||||||
text: S,
|
text: S,
|
||||||
headline: Option<&'static str>,
|
headline: Option<&'static str>,
|
||||||
button_text: Option<&'static str>,
|
button_text: Option<&'static str>,
|
||||||
|
@ -4,9 +4,9 @@ use crate::{
|
|||||||
component::{
|
component::{
|
||||||
Component, ComponentExt, Event, EventCtx, FixedHeightBar, GridPlaced, Map, TimerToken,
|
Component, ComponentExt, Event, EventCtx, FixedHeightBar, GridPlaced, Map, TimerToken,
|
||||||
},
|
},
|
||||||
display::{self, Color, Font},
|
display::{self, toif::Icon, Color, Font},
|
||||||
event::TouchEvent,
|
event::TouchEvent,
|
||||||
geometry::{Insets, Offset, Rect},
|
geometry::{Insets, Offset, Rect, CENTER},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,11 +48,11 @@ impl<T> Button<T> {
|
|||||||
Self::new(ButtonContent::Text(text))
|
Self::new(ButtonContent::Text(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_icon(image: &'static [u8]) -> Self {
|
pub fn with_icon(icon: Icon) -> Self {
|
||||||
Self::new(ButtonContent::Icon(image))
|
Self::new(ButtonContent::Icon(icon))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_icon_blend(bg: &'static [u8], fg: &'static [u8], fg_offset: Offset) -> Self {
|
pub fn with_icon_blend(bg: Icon, fg: Icon, fg_offset: Offset) -> Self {
|
||||||
Self::new(ButtonContent::IconBlend(bg, fg, fg_offset))
|
Self::new(ButtonContent::IconBlend(bg, fg, fg_offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,17 +198,17 @@ impl<T> Button<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ButtonContent::Icon(icon) => {
|
ButtonContent::Icon(icon) => {
|
||||||
display::icon(
|
icon.draw(
|
||||||
self.area.center(),
|
self.area.center(),
|
||||||
icon,
|
CENTER,
|
||||||
style.text_color,
|
style.text_color,
|
||||||
style.button_color,
|
style.button_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ButtonContent::IconBlend(bg, fg, offset) => display::icon_over_icon(
|
ButtonContent::IconBlend(bg, fg, offset) => display::icon_over_icon(
|
||||||
Some(self.area),
|
Some(self.area),
|
||||||
(bg, Offset::zero(), style.button_color),
|
(*bg, Offset::zero(), style.button_color),
|
||||||
(fg, *offset, style.text_color),
|
(*fg, *offset, style.text_color),
|
||||||
style.background_color,
|
style.background_color,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -328,8 +328,8 @@ enum State {
|
|||||||
pub enum ButtonContent<T> {
|
pub enum ButtonContent<T> {
|
||||||
Empty,
|
Empty,
|
||||||
Text(T),
|
Text(T),
|
||||||
Icon(&'static [u8]),
|
Icon(Icon),
|
||||||
IconBlend(&'static [u8], &'static [u8], Offset),
|
IconBlend(Icon, Icon, Offset),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
@ -396,7 +396,7 @@ impl<T> Button<T> {
|
|||||||
let (left, right_size_factor) = if let Some(verb) = left {
|
let (left, right_size_factor) = if let Some(verb) = left {
|
||||||
(Button::with_text(verb), 1)
|
(Button::with_text(verb), 1)
|
||||||
} else {
|
} else {
|
||||||
(Button::with_icon(theme::ICON_CANCEL), 2)
|
(Button::with_icon(Icon::new(theme::ICON_CANCEL)), 2)
|
||||||
};
|
};
|
||||||
let right = Button::with_text(right).styled(theme::button_confirm());
|
let right = Button::with_text(right).styled(theme::button_confirm());
|
||||||
|
|
||||||
@ -417,7 +417,7 @@ impl<T> Button<T> {
|
|||||||
{
|
{
|
||||||
let right = Button::with_text(confirm).styled(theme::button_confirm());
|
let right = Button::with_text(confirm).styled(theme::button_confirm());
|
||||||
let top = Button::with_text(info);
|
let top = Button::with_text(info);
|
||||||
let left = Button::with_icon(theme::ICON_CANCEL);
|
let left = Button::with_icon(Icon::new(theme::ICON_CANCEL));
|
||||||
theme::button_bar_rows(
|
theme::button_bar_rows(
|
||||||
2,
|
2,
|
||||||
(
|
(
|
||||||
|
@ -6,6 +6,7 @@ use crate::ui::{
|
|||||||
},
|
},
|
||||||
Child, Component, Event, EventCtx, Never,
|
Child, Component, Event, EventCtx, Never,
|
||||||
},
|
},
|
||||||
|
display::toif::Icon,
|
||||||
geometry::{Insets, LinearPlacement, Rect},
|
geometry::{Insets, LinearPlacement, Rect},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,8 +129,8 @@ where
|
|||||||
let [l0, l1, l2, l3] = lines;
|
let [l0, l1, l2, l3] = lines;
|
||||||
Self {
|
Self {
|
||||||
image: Child::new(BlendedImage::new(
|
image: Child::new(BlendedImage::new(
|
||||||
theme::IMAGE_BG_CIRCLE,
|
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||||
theme::IMAGE_FG_SUCCESS,
|
Icon::new(theme::IMAGE_FG_SUCCESS),
|
||||||
theme::SUCCESS_COLOR,
|
theme::SUCCESS_COLOR,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Image, Label},
|
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
||||||
display,
|
display,
|
||||||
geometry::{Alignment, Insets, Rect},
|
geometry::{Alignment, Insets, Rect},
|
||||||
model_tt::component::{
|
model_tt::component::{
|
||||||
|
@ -2,42 +2,45 @@
|
|||||||
//! (by running `make templates` in `core`)
|
//! (by running `make templates` in `core`)
|
||||||
//! do not edit manually!
|
//! do not edit manually!
|
||||||
|
|
||||||
const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif");
|
use crate::ui::display::toif::NamedToif;
|
||||||
const ICON_BINANCE: &[u8] = include_res!("model_tt/res/fido/icon_binance.toif");
|
|
||||||
const ICON_BITBUCKET: &[u8] = include_res!("model_tt/res/fido/icon_bitbucket.toif");
|
|
||||||
const ICON_BITFINEX: &[u8] = include_res!("model_tt/res/fido/icon_bitfinex.toif");
|
const ICON_AWS: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_aws.toif"), "AWS");
|
||||||
const ICON_BITWARDEN: &[u8] = include_res!("model_tt/res/fido/icon_bitwarden.toif");
|
const ICON_BINANCE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_binance.toif"), "BINANCE");
|
||||||
const ICON_CLOUDFLARE: &[u8] = include_res!("model_tt/res/fido/icon_cloudflare.toif");
|
const ICON_BITBUCKET: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitbucket.toif"), "BITBUCKET");
|
||||||
const ICON_COINBASE: &[u8] = include_res!("model_tt/res/fido/icon_coinbase.toif");
|
const ICON_BITFINEX: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitfinex.toif"), "BITFINEX");
|
||||||
const ICON_DASHLANE: &[u8] = include_res!("model_tt/res/fido/icon_dashlane.toif");
|
const ICON_BITWARDEN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitwarden.toif"), "BITWARDEN");
|
||||||
const ICON_DROPBOX: &[u8] = include_res!("model_tt/res/fido/icon_dropbox.toif");
|
const ICON_CLOUDFLARE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_cloudflare.toif"), "CLOUDFLARE");
|
||||||
const ICON_DUO: &[u8] = include_res!("model_tt/res/fido/icon_duo.toif");
|
const ICON_COINBASE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_coinbase.toif"), "COINBASE");
|
||||||
const ICON_FACEBOOK: &[u8] = include_res!("model_tt/res/fido/icon_facebook.toif");
|
const ICON_DASHLANE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_dashlane.toif"), "DASHLANE");
|
||||||
const ICON_FASTMAIL: &[u8] = include_res!("model_tt/res/fido/icon_fastmail.toif");
|
const ICON_DROPBOX: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_dropbox.toif"), "DROPBOX");
|
||||||
const ICON_FEDORA: &[u8] = include_res!("model_tt/res/fido/icon_fedora.toif");
|
const ICON_DUO: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_duo.toif"), "DUO");
|
||||||
const ICON_GANDI: &[u8] = include_res!("model_tt/res/fido/icon_gandi.toif");
|
const ICON_FACEBOOK: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_facebook.toif"), "FACEBOOK");
|
||||||
const ICON_GEMINI: &[u8] = include_res!("model_tt/res/fido/icon_gemini.toif");
|
const ICON_FASTMAIL: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_fastmail.toif"), "FASTMAIL");
|
||||||
const ICON_GITHUB: &[u8] = include_res!("model_tt/res/fido/icon_github.toif");
|
const ICON_FEDORA: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_fedora.toif"), "FEDORA");
|
||||||
const ICON_GITLAB: &[u8] = include_res!("model_tt/res/fido/icon_gitlab.toif");
|
const ICON_GANDI: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gandi.toif"), "GANDI");
|
||||||
const ICON_GOOGLE: &[u8] = include_res!("model_tt/res/fido/icon_google.toif");
|
const ICON_GEMINI: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gemini.toif"), "GEMINI");
|
||||||
const ICON_INVITY: &[u8] = include_res!("model_tt/res/fido/icon_invity.toif");
|
const ICON_GITHUB: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_github.toif"), "GITHUB");
|
||||||
const ICON_KEEPER: &[u8] = include_res!("model_tt/res/fido/icon_keeper.toif");
|
const ICON_GITLAB: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gitlab.toif"), "GITLAB");
|
||||||
const ICON_KRAKEN: &[u8] = include_res!("model_tt/res/fido/icon_kraken.toif");
|
const ICON_GOOGLE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_google.toif"), "GOOGLE");
|
||||||
const ICON_LOGIN_GOV: &[u8] = include_res!("model_tt/res/fido/icon_login.gov.toif");
|
const ICON_INVITY: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_invity.toif"), "INVITY");
|
||||||
const ICON_MICROSOFT: &[u8] = include_res!("model_tt/res/fido/icon_microsoft.toif");
|
const ICON_KEEPER: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_keeper.toif"), "KEEPER");
|
||||||
const ICON_MOJEID: &[u8] = include_res!("model_tt/res/fido/icon_mojeid.toif");
|
const ICON_KRAKEN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_kraken.toif"), "KRAKEN");
|
||||||
const ICON_NAMECHEAP: &[u8] = include_res!("model_tt/res/fido/icon_namecheap.toif");
|
const ICON_LOGIN_GOV: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_login.gov.toif"), "LOGIN_GOV");
|
||||||
const ICON_PROTON: &[u8] = include_res!("model_tt/res/fido/icon_proton.toif");
|
const ICON_MICROSOFT: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_microsoft.toif"), "MICROSOFT");
|
||||||
const ICON_SLUSHPOOL: &[u8] = include_res!("model_tt/res/fido/icon_slushpool.toif");
|
const ICON_MOJEID: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_mojeid.toif"), "MOJEID");
|
||||||
const ICON_STRIPE: &[u8] = include_res!("model_tt/res/fido/icon_stripe.toif");
|
const ICON_NAMECHEAP: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_namecheap.toif"), "NAMECHEAP");
|
||||||
const ICON_TUTANOTA: &[u8] = include_res!("model_tt/res/fido/icon_tutanota.toif");
|
const ICON_PROTON: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_proton.toif"), "PROTON");
|
||||||
|
const ICON_SLUSHPOOL: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_slushpool.toif"), "SLUSHPOOL");
|
||||||
|
const ICON_STRIPE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_stripe.toif"), "STRIPE");
|
||||||
|
const ICON_TUTANOTA: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_tutanota.toif"), "TUTANOTA");
|
||||||
/// Default icon when app does not have its own
|
/// Default icon when app does not have its own
|
||||||
const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif");
|
const ICON_WEBAUTHN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_webauthn.toif"), "WEBAUTHN");
|
||||||
|
|
||||||
/// Translates icon name into its data.
|
/// Translates icon name into its data.
|
||||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||||
/// supplied.
|
/// supplied.
|
||||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> NamedToif {
|
||||||
if let Some(icon_name) = icon_name {
|
if let Some(icon_name) = icon_name {
|
||||||
match icon_name.as_ref() {
|
match icon_name.as_ref() {
|
||||||
"aws" => ICON_AWS,
|
"aws" => ICON_AWS,
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
//! generated from webauthn_icons.rs.mako
|
//! generated from webauthn_icons.rs.mako
|
||||||
//! (by running `make templates` in `core`)
|
//! (by running `make templates` in `core`)
|
||||||
//! do not edit manually!
|
//! do not edit manually!
|
||||||
|
|
||||||
|
use crate::ui::display::toif::NamedToif;
|
||||||
|
|
||||||
<%
|
<%
|
||||||
icons: list[tuple[str, str]] = []
|
icons: list[tuple[str, str]] = []
|
||||||
for app in fido:
|
for app in fido:
|
||||||
@ -12,15 +15,15 @@ for app in fido:
|
|||||||
%>\
|
%>\
|
||||||
|
|
||||||
% for icon_name, var_name in icons:
|
% for icon_name, var_name in icons:
|
||||||
const ICON_${var_name}: &[u8] = include_res!("model_tt/res/fido/icon_${icon_name}.toif");
|
const ICON_${var_name}: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_${icon_name}.toif"), "${var_name}");
|
||||||
% endfor
|
% endfor
|
||||||
/// Default icon when app does not have its own
|
/// Default icon when app does not have its own
|
||||||
const ICON_WEBAUTHN: &[u8] = include_res!("model_tt/res/fido/icon_webauthn.toif");
|
const ICON_WEBAUTHN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_webauthn.toif"), "WEBAUTHN");
|
||||||
|
|
||||||
/// Translates icon name into its data.
|
/// Translates icon name into its data.
|
||||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||||
/// supplied.
|
/// supplied.
|
||||||
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> &'static [u8] {
|
pub fn get_fido_icon_data<T: AsRef<str>>(icon_name: Option<T>) -> NamedToif {
|
||||||
if let Some(icon_name) = icon_name {
|
if let Some(icon_name) = icon_name {
|
||||||
match icon_name.as_ref() {
|
match icon_name.as_ref() {
|
||||||
% for icon_name, var_name in icons:
|
% for icon_name, var_name in icons:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::theme;
|
use super::theme;
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{label::Label, text::TextStyle, Child, Component, Event, EventCtx},
|
component::{label::Label, text::TextStyle, Child, Component, Event, EventCtx},
|
||||||
display::{self, Color, Font},
|
display::{self, toif::Icon, Color, Font},
|
||||||
geometry::{Alignment, Insets, Offset, Rect},
|
geometry::{Alignment, Insets, Offset, Rect},
|
||||||
util::icon_text_center,
|
util::icon_text_center,
|
||||||
};
|
};
|
||||||
@ -100,7 +100,7 @@ where
|
|||||||
|
|
||||||
pub struct NotificationFrame<T, U> {
|
pub struct NotificationFrame<T, U> {
|
||||||
area: Rect,
|
area: Rect,
|
||||||
icon: &'static [u8],
|
icon: Icon,
|
||||||
title: U,
|
title: U,
|
||||||
content: Child<T>,
|
content: Child<T>,
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ where
|
|||||||
const ICON_SPACE: i16 = 8;
|
const ICON_SPACE: i16 = 8;
|
||||||
const BORDER: i16 = 8;
|
const BORDER: i16 = 8;
|
||||||
|
|
||||||
pub fn new(icon: &'static [u8], title: U, content: T) -> Self {
|
pub fn new(icon: Icon, title: U, content: T) -> Self {
|
||||||
Self {
|
Self {
|
||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
@ -129,7 +129,7 @@ where
|
|||||||
self.content.inner()
|
self.content.inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint_notification(area: Rect, icon: &'static [u8], title: &str, color: Color) {
|
pub fn paint_notification(area: Rect, icon: Icon, title: &str, color: Color) {
|
||||||
let (area, _) = area
|
let (area, _) = area
|
||||||
.inset(Insets::uniform(Self::BORDER))
|
.inset(Insets::uniform(Self::BORDER))
|
||||||
.split_top(Self::HEIGHT);
|
.split_top(Self::HEIGHT);
|
||||||
|
@ -2,6 +2,7 @@ use crate::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
ui::{
|
ui::{
|
||||||
component::{Child, Component, ComponentExt, Event, EventCtx, FixedHeightBar, Pad},
|
component::{Child, Component, ComponentExt, Event, EventCtx, FixedHeightBar, Pad},
|
||||||
|
display::toif::Icon,
|
||||||
geometry::{Grid, Insets, Rect},
|
geometry::{Grid, Insets, Rect},
|
||||||
util::animation_disabled,
|
util::animation_disabled,
|
||||||
},
|
},
|
||||||
@ -126,7 +127,7 @@ pub enum CancelHoldMsg {
|
|||||||
impl CancelHold {
|
impl CancelHold {
|
||||||
pub fn new(button_style: ButtonStyleSheet) -> FixedHeightBar<Self> {
|
pub fn new(button_style: ButtonStyleSheet) -> FixedHeightBar<Self> {
|
||||||
theme::button_bar(Self {
|
theme::button_bar(Self {
|
||||||
cancel: Some(Button::with_icon(theme::ICON_CANCEL).into_child()),
|
cancel: Some(Button::with_icon(Icon::new(theme::ICON_CANCEL)).into_child()),
|
||||||
hold: Button::with_text("HOLD TO CONFIRM")
|
hold: Button::with_text("HOLD TO CONFIRM")
|
||||||
.styled(button_style)
|
.styled(button_style)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
trezorhal::usb::usb_configured,
|
trezorhal::usb::usb_configured,
|
||||||
ui::{
|
ui::{
|
||||||
component::{Component, Event, EventCtx, Pad, TimerToken},
|
component::{Component, Event, EventCtx, Pad, TimerToken},
|
||||||
display::{self, tjpgd::jpeg_info, Color, Font},
|
display::{self, tjpgd::jpeg_info, toif::Icon, Color, Font},
|
||||||
event::{TouchEvent, USBEvent},
|
event::{TouchEvent, USBEvent},
|
||||||
geometry::{Offset, Point, Rect},
|
geometry::{Offset, Point, Rect},
|
||||||
model_tt::{constant, theme::IMAGE_HOMESCREEN},
|
model_tt::{constant, theme::IMAGE_HOMESCREEN},
|
||||||
@ -60,11 +60,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn level_to_style(level: u8) -> (Color, &'static [u8]) {
|
fn level_to_style(level: u8) -> (Color, Icon) {
|
||||||
match level {
|
match level {
|
||||||
2 => (theme::VIOLET, theme::ICON_MAGIC),
|
2 => (theme::VIOLET, Icon::new(theme::ICON_MAGIC)),
|
||||||
1 => (theme::YELLOW, theme::ICON_WARN),
|
1 => (theme::YELLOW, Icon::new(theme::ICON_WARN)),
|
||||||
_ => (theme::RED, theme::ICON_WARN),
|
_ => (theme::RED, Icon::new(theme::ICON_WARN)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ where
|
|||||||
text: locked,
|
text: locked,
|
||||||
style: theme::TEXT_BOLD,
|
style: theme::TEXT_BOLD,
|
||||||
offset: Offset::new(10, LOCKED_Y),
|
offset: Offset::new(10, LOCKED_Y),
|
||||||
icon: Some(theme::ICON_LOCK),
|
icon: Some(Icon::new(theme::ICON_LOCK)),
|
||||||
},
|
},
|
||||||
HomescreenText {
|
HomescreenText {
|
||||||
text: tap,
|
text: tap,
|
||||||
|
@ -7,12 +7,11 @@ use crate::{
|
|||||||
trezorhal::{
|
trezorhal::{
|
||||||
buffers::{get_blurring_buffer, get_jpeg_buffer, get_jpeg_work_buffer, BufferJpeg},
|
buffers::{get_blurring_buffer, get_jpeg_buffer, get_jpeg_work_buffer, BufferJpeg},
|
||||||
display,
|
display,
|
||||||
display::{bar_radius_buffer, ToifFormat},
|
display::bar_radius_buffer,
|
||||||
uzlib::UzlibContext,
|
|
||||||
},
|
},
|
||||||
ui::{
|
ui::{
|
||||||
constant::screen,
|
constant::screen,
|
||||||
display::{position_buffer, set_window, toif_info_ensure, Color},
|
display::{position_buffer, set_window, Color},
|
||||||
geometry::{Offset, Point, Rect},
|
geometry::{Offset, Point, Rect},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -20,7 +19,10 @@ use crate::{
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::text::TextStyle,
|
component::text::TextStyle,
|
||||||
constant::{HEIGHT, WIDTH},
|
constant::{HEIGHT, WIDTH},
|
||||||
display::tjpgd::{BufferInput, BufferOutput, JDEC},
|
display::{
|
||||||
|
tjpgd::{BufferInput, BufferOutput, JDEC},
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
model_tt::theme,
|
model_tt::theme,
|
||||||
util::icon_text_center,
|
util::icon_text_center,
|
||||||
};
|
};
|
||||||
@ -30,13 +32,13 @@ pub struct HomescreenText<'a> {
|
|||||||
pub text: &'a str,
|
pub text: &'a str,
|
||||||
pub style: TextStyle,
|
pub style: TextStyle,
|
||||||
pub offset: Offset,
|
pub offset: Offset,
|
||||||
pub icon: Option<&'static [u8]>,
|
pub icon: Option<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct HomescreenNotification<'a> {
|
pub struct HomescreenNotification<'a> {
|
||||||
pub text: &'a str,
|
pub text: &'a str,
|
||||||
pub icon: &'static [u8],
|
pub icon: Icon,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,12 +131,10 @@ fn homescreen_position_text(
|
|||||||
let text_width_clamped = text_width.clamp(0, screen().width());
|
let text_width_clamped = text_width.clamp(0, screen().width());
|
||||||
|
|
||||||
let icon_size = if let Some(icon) = text.icon {
|
let icon_size = if let Some(icon) = text.icon {
|
||||||
let (icon_size, icon_data) = toif_info_ensure(icon, ToifFormat::GrayScaleEH);
|
assert!(icon.toif.width() <= HOMESCREEN_MAX_ICON_SIZE);
|
||||||
assert!(icon_size.x <= HOMESCREEN_MAX_ICON_SIZE);
|
assert!(icon.toif.height() <= HOMESCREEN_MAX_ICON_SIZE);
|
||||||
assert!(icon_size.y <= HOMESCREEN_MAX_ICON_SIZE);
|
icon.toif.uncompress(icon_buffer);
|
||||||
let mut ctx = UzlibContext::new(icon_data, None);
|
icon.toif.size
|
||||||
unwrap!(ctx.uncompress(icon_buffer), "Decompression failed");
|
|
||||||
icon_size
|
|
||||||
} else {
|
} else {
|
||||||
Offset::zero()
|
Offset::zero()
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,8 @@ use crate::{
|
|||||||
ui::{
|
ui::{
|
||||||
component::{Component, Event, EventCtx},
|
component::{Component, Event, EventCtx},
|
||||||
display,
|
display,
|
||||||
geometry::{Offset, Rect},
|
display::toif::Icon,
|
||||||
|
geometry::{Offset, Rect, CENTER},
|
||||||
model_tt::{
|
model_tt::{
|
||||||
component::{
|
component::{
|
||||||
keyboard::{
|
keyboard::{
|
||||||
@ -139,7 +140,7 @@ impl Component for Bip39Input {
|
|||||||
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
||||||
// 16px from the right edge.
|
// 16px from the right edge.
|
||||||
let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0);
|
let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0);
|
||||||
display::icon(icon_center, icon, style.text_color, style.button_color);
|
icon.draw(icon_center, CENTER, style.text_color, style.button_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,13 +217,13 @@ impl Bip39Input {
|
|||||||
self.button.enable(ctx);
|
self.button.enable(ctx);
|
||||||
self.button.set_stylesheet(ctx, theme::button_confirm());
|
self.button.set_stylesheet(ctx, theme::button_confirm());
|
||||||
self.button
|
self.button
|
||||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM));
|
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||||
} else {
|
} else {
|
||||||
// Auto-complete button.
|
// Auto-complete button.
|
||||||
self.button.enable(ctx);
|
self.button.enable(ctx);
|
||||||
self.button.set_stylesheet(ctx, theme::button_default());
|
self.button.set_stylesheet(ctx, theme::button_default());
|
||||||
self.button
|
self.button
|
||||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CLICK));
|
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CLICK)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Disabled button.
|
// Disabled button.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
component::{maybe::paint_overlapping, Child, Component, Event, EventCtx, Label, Maybe},
|
||||||
geometry::{Alignment, Grid, Offset, Rect},
|
display::toif::Icon,
|
||||||
|
geometry::{Grid, Offset, Rect, CENTER},
|
||||||
model_tt::{
|
model_tt::{
|
||||||
component::{Button, ButtonMsg},
|
component::{Button, ButtonMsg},
|
||||||
theme,
|
theme,
|
||||||
@ -38,8 +39,8 @@ where
|
|||||||
back: Child::new(Maybe::hidden(
|
back: Child::new(Maybe::hidden(
|
||||||
theme::BG,
|
theme::BG,
|
||||||
Button::with_icon_blend(
|
Button::with_icon_blend(
|
||||||
theme::IMAGE_BG_BACK_BTN_TALL,
|
Icon::new(theme::IMAGE_BG_BACK_BTN_TALL),
|
||||||
theme::ICON_BACK,
|
Icon::new(theme::ICON_BACK),
|
||||||
Offset::new(30, 17),
|
Offset::new(30, 17),
|
||||||
)
|
)
|
||||||
.styled(theme::button_clear())
|
.styled(theme::button_clear())
|
||||||
@ -100,8 +101,7 @@ where
|
|||||||
|
|
||||||
let prompt_center = grid.row_col(0, 0).union(grid.row_col(0, 3)).center();
|
let prompt_center = grid.row_col(0, 0).union(grid.row_col(0, 3)).center();
|
||||||
let prompt_size = self.prompt.inner().inner().max_size();
|
let prompt_size = self.prompt.inner().inner().max_size();
|
||||||
let prompt_top_left = prompt_size.snap(prompt_center, Alignment::Center, Alignment::Center);
|
let prompt_area = Rect::snap(prompt_center, prompt_size, CENTER);
|
||||||
let prompt_area = Rect::from_top_left_and_size(prompt_top_left, prompt_size);
|
|
||||||
|
|
||||||
self.prompt.place(prompt_area);
|
self.prompt.place(prompt_area);
|
||||||
self.back.place(back_area);
|
self.back.place(back_area);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{base::ComponentExt, Child, Component, Event, EventCtx, Never},
|
component::{base::ComponentExt, Child, Component, Event, EventCtx, Never},
|
||||||
display,
|
display,
|
||||||
|
display::toif::Icon,
|
||||||
geometry::{Grid, Insets, Offset, Rect},
|
geometry::{Grid, Insets, Offset, Rect},
|
||||||
model_tt::component::{
|
model_tt::component::{
|
||||||
button::{Button, ButtonContent, ButtonMsg},
|
button::{Button, ButtonContent, ButtonMsg},
|
||||||
@ -46,12 +47,12 @@ impl PassphraseKeyboard {
|
|||||||
Self {
|
Self {
|
||||||
page_swipe: Swipe::horizontal(),
|
page_swipe: Swipe::horizontal(),
|
||||||
input: Input::new().into_child(),
|
input: Input::new().into_child(),
|
||||||
confirm: Button::with_icon(theme::ICON_CONFIRM)
|
confirm: Button::with_icon(Icon::new(theme::ICON_CONFIRM))
|
||||||
.styled(theme::button_confirm())
|
.styled(theme::button_confirm())
|
||||||
.into_child(),
|
.into_child(),
|
||||||
back: Button::with_icon_blend(
|
back: Button::with_icon_blend(
|
||||||
theme::IMAGE_BG_BACK_BTN,
|
Icon::new(theme::IMAGE_BG_BACK_BTN),
|
||||||
theme::ICON_BACK,
|
Icon::new(theme::ICON_BACK),
|
||||||
Offset::new(30, 12),
|
Offset::new(30, 12),
|
||||||
)
|
)
|
||||||
.styled(theme::button_reset())
|
.styled(theme::button_reset())
|
||||||
@ -61,7 +62,7 @@ impl PassphraseKeyboard {
|
|||||||
keys: KEYBOARD.map(|page| {
|
keys: KEYBOARD.map(|page| {
|
||||||
page.map(|text| {
|
page.map(|text| {
|
||||||
if text == " " {
|
if text == " " {
|
||||||
let icon = theme::ICON_SPACE;
|
let icon = Icon::new(theme::ICON_SPACE);
|
||||||
Child::new(Button::with_icon(icon))
|
Child::new(Button::with_icon(icon))
|
||||||
} else {
|
} else {
|
||||||
Child::new(Button::with_text(text))
|
Child::new(Button::with_text(text))
|
||||||
|
@ -9,9 +9,9 @@ use crate::{
|
|||||||
base::ComponentExt, text::TextStyle, Child, Component, Event, EventCtx, Label, Maybe,
|
base::ComponentExt, text::TextStyle, Child, Component, Event, EventCtx, Label, Maybe,
|
||||||
Never, Pad, TimerToken,
|
Never, Pad, TimerToken,
|
||||||
},
|
},
|
||||||
display::{self, Font},
|
display::{self, toif::Icon, Font},
|
||||||
event::TouchEvent,
|
event::TouchEvent,
|
||||||
geometry::{Alignment, Grid, Insets, Offset, Rect},
|
geometry::{Grid, Insets, Offset, Rect, CENTER, TOP_LEFT},
|
||||||
model_tt::component::{
|
model_tt::component::{
|
||||||
button::{Button, ButtonContent, ButtonMsg, ButtonMsg::Clicked},
|
button::{Button, ButtonContent, ButtonMsg, ButtonMsg::Clicked},
|
||||||
theme,
|
theme,
|
||||||
@ -70,8 +70,8 @@ where
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
// Control buttons.
|
// Control buttons.
|
||||||
let erase_btn = Button::with_icon_blend(
|
let erase_btn = Button::with_icon_blend(
|
||||||
theme::IMAGE_BG_BACK_BTN,
|
Icon::new(theme::IMAGE_BG_BACK_BTN),
|
||||||
theme::ICON_BACK,
|
Icon::new(theme::ICON_BACK),
|
||||||
Offset::new(30, 12),
|
Offset::new(30, 12),
|
||||||
)
|
)
|
||||||
.styled(theme::button_reset())
|
.styled(theme::button_reset())
|
||||||
@ -79,7 +79,8 @@ where
|
|||||||
.initially_enabled(false);
|
.initially_enabled(false);
|
||||||
let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child();
|
let erase_btn = Maybe::hidden(theme::BG, erase_btn).into_child();
|
||||||
|
|
||||||
let cancel_btn = Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel());
|
let cancel_btn =
|
||||||
|
Button::with_icon(Icon::new(theme::ICON_CANCEL)).styled(theme::button_cancel());
|
||||||
let cancel_btn =
|
let cancel_btn =
|
||||||
Maybe::new(Pad::with_background(theme::BG), cancel_btn, allow_cancel).into_child();
|
Maybe::new(Pad::with_background(theme::BG), cancel_btn, allow_cancel).into_child();
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ where
|
|||||||
textbox_pad: Pad::with_background(theme::label_default().background_color),
|
textbox_pad: Pad::with_background(theme::label_default().background_color),
|
||||||
erase_btn,
|
erase_btn,
|
||||||
cancel_btn,
|
cancel_btn,
|
||||||
confirm_btn: Button::with_icon(theme::ICON_CONFIRM)
|
confirm_btn: Button::with_icon(Icon::new(theme::ICON_CONFIRM))
|
||||||
.styled(theme::button_confirm())
|
.styled(theme::button_confirm())
|
||||||
.initially_enabled(false)
|
.initially_enabled(false)
|
||||||
.into_child(),
|
.into_child(),
|
||||||
@ -369,9 +370,7 @@ impl PinDots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint_dots(&self, area: Rect) {
|
fn paint_dots(&self, area: Rect) {
|
||||||
let mut cursor = self
|
let mut cursor = self.size().snap(area.center(), CENTER);
|
||||||
.size()
|
|
||||||
.snap(area.center(), Alignment::Center, Alignment::Center);
|
|
||||||
|
|
||||||
let digits = self.digits.len();
|
let digits = self.digits.len();
|
||||||
let dots_visible = digits.min(MAX_VISIBLE_DOTS);
|
let dots_visible = digits.min(MAX_VISIBLE_DOTS);
|
||||||
@ -384,9 +383,9 @@ impl PinDots {
|
|||||||
|
|
||||||
// Small leftmost dot.
|
// Small leftmost dot.
|
||||||
if digits > dots_visible + 1 {
|
if digits > dots_visible + 1 {
|
||||||
display::icon_top_left(
|
Icon::new(theme::DOT_SMALL).draw(
|
||||||
cursor - Offset::x(2 * step),
|
cursor - Offset::x(2 * step),
|
||||||
theme::DOT_SMALL,
|
TOP_LEFT,
|
||||||
self.style.text_color,
|
self.style.text_color,
|
||||||
self.style.background_color,
|
self.style.background_color,
|
||||||
);
|
);
|
||||||
@ -394,9 +393,9 @@ impl PinDots {
|
|||||||
|
|
||||||
// Greyed out dot.
|
// Greyed out dot.
|
||||||
if digits > dots_visible {
|
if digits > dots_visible {
|
||||||
display::icon_top_left(
|
Icon::new(theme::DOT_ACTIVE).draw(
|
||||||
cursor - Offset::x(step),
|
cursor - Offset::x(step),
|
||||||
theme::DOT_ACTIVE,
|
TOP_LEFT,
|
||||||
theme::GREY_LIGHT,
|
theme::GREY_LIGHT,
|
||||||
self.style.background_color,
|
self.style.background_color,
|
||||||
);
|
);
|
||||||
@ -404,9 +403,9 @@ impl PinDots {
|
|||||||
|
|
||||||
// Draw a dot for each PIN digit.
|
// Draw a dot for each PIN digit.
|
||||||
for _ in 0..dots_visible {
|
for _ in 0..dots_visible {
|
||||||
display::icon_top_left(
|
Icon::new(theme::DOT_ACTIVE).draw(
|
||||||
cursor,
|
cursor,
|
||||||
theme::DOT_ACTIVE,
|
TOP_LEFT,
|
||||||
self.style.text_color,
|
self.style.text_color,
|
||||||
self.style.background_color,
|
self.style.background_color,
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,8 @@ use crate::{
|
|||||||
ui::{
|
ui::{
|
||||||
component::{Component, Event, EventCtx},
|
component::{Component, Event, EventCtx},
|
||||||
display,
|
display,
|
||||||
geometry::{Offset, Rect},
|
display::toif::Icon,
|
||||||
|
geometry::{Offset, Rect, CENTER},
|
||||||
model_tt::{
|
model_tt::{
|
||||||
component::{
|
component::{
|
||||||
keyboard::{
|
keyboard::{
|
||||||
@ -173,7 +174,7 @@ impl Component for Slip39Input {
|
|||||||
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
||||||
// 16px from the right edge.
|
// 16px from the right edge.
|
||||||
let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0);
|
let icon_center = area.top_right().center(area.bottom_right()) - Offset::new(16 + 8, 0);
|
||||||
display::icon(icon_center, icon, style.text_color, style.button_color);
|
icon.draw(icon_center, CENTER, style.text_color, style.button_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +226,7 @@ impl Slip39Input {
|
|||||||
self.button.enable(ctx);
|
self.button.enable(ctx);
|
||||||
self.button.set_stylesheet(ctx, theme::button_confirm());
|
self.button.set_stylesheet(ctx, theme::button_confirm());
|
||||||
self.button
|
self.button
|
||||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM));
|
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||||
} else {
|
} else {
|
||||||
// Disabled button.
|
// Disabled button.
|
||||||
self.button.disable(ctx);
|
self.button.disable(ctx);
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
ui::{
|
ui::{
|
||||||
animation::Animation,
|
animation::Animation,
|
||||||
component::{Component, Event, EventCtx},
|
component::{Component, Event, EventCtx},
|
||||||
display::{self, Color},
|
display::{self, toif::Icon, Color},
|
||||||
geometry::{Offset, Rect},
|
geometry::{Offset, Rect},
|
||||||
model_tt::constant,
|
model_tt::constant,
|
||||||
util::animation_disabled,
|
util::animation_disabled,
|
||||||
@ -173,6 +173,7 @@ impl Component for Loader {
|
|||||||
} else {
|
} else {
|
||||||
self.styles.active
|
self.styles.active
|
||||||
};
|
};
|
||||||
|
|
||||||
display::loader(
|
display::loader(
|
||||||
progress,
|
progress,
|
||||||
self.offset_y,
|
self.offset_y,
|
||||||
@ -190,7 +191,7 @@ pub struct LoaderStyleSheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct LoaderStyle {
|
pub struct LoaderStyle {
|
||||||
pub icon: Option<(&'static [u8], Color)>,
|
pub icon: Option<(Icon, Color)>,
|
||||||
pub loader_color: Color,
|
pub loader_color: Color,
|
||||||
pub background_color: Color,
|
pub background_color: Color,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod button;
|
mod button;
|
||||||
mod dialog;
|
mod dialog;
|
||||||
mod fido;
|
mod fido;
|
||||||
|
#[rustfmt::skip]
|
||||||
mod fido_icons;
|
mod fido_icons;
|
||||||
mod frame;
|
mod frame;
|
||||||
mod hold_to_confirm;
|
mod hold_to_confirm;
|
||||||
|
@ -3,7 +3,7 @@ use crate::ui::{
|
|||||||
base::ComponentExt, paginated::PageMsg, Component, Event, EventCtx, FixedHeightBar, Label,
|
base::ComponentExt, paginated::PageMsg, Component, Event, EventCtx, FixedHeightBar, Label,
|
||||||
Pad, Paginate,
|
Pad, Paginate,
|
||||||
},
|
},
|
||||||
display::{self, Color},
|
display::{self, toif::Icon, Color},
|
||||||
geometry::{Insets, Rect},
|
geometry::{Insets, Rect},
|
||||||
model_tt::component::{Button, ButtonMsg},
|
model_tt::component::{Button, ButtonMsg},
|
||||||
};
|
};
|
||||||
@ -44,7 +44,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_back_button(mut self) -> Self {
|
pub fn with_back_button(mut self) -> Self {
|
||||||
self.button_back = Some(Button::with_icon(theme::ICON_BACK));
|
self.button_back = Some(Button::with_icon(Icon::new(theme::ICON_BACK)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Component, Event, EventCtx, Never},
|
component::{Component, Event, EventCtx, Never},
|
||||||
display,
|
display::toif::Icon,
|
||||||
geometry::{LinearPlacement, Offset, Rect},
|
geometry::{LinearPlacement, Offset, Rect, CENTER},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::theme;
|
use super::theme;
|
||||||
@ -76,11 +76,11 @@ impl Component for ScrollBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self) {
|
fn paint(&mut self) {
|
||||||
fn dotsize(distance: usize, nhidden: usize) -> &'static [u8] {
|
fn dotsize(distance: usize, nhidden: usize) -> Icon {
|
||||||
match (nhidden.saturating_sub(distance)).min(2 - distance) {
|
match (nhidden.saturating_sub(distance)).min(2 - distance) {
|
||||||
0 => theme::DOT_INACTIVE,
|
0 => Icon::new(theme::DOT_INACTIVE),
|
||||||
1 => theme::DOT_INACTIVE_HALF,
|
1 => Icon::new(theme::DOT_INACTIVE_HALF),
|
||||||
_ => theme::DOT_INACTIVE_QUARTER,
|
_ => Icon::new(theme::DOT_INACTIVE_QUARTER),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ impl Component for ScrollBar {
|
|||||||
);
|
);
|
||||||
for i in first_shown..(last_shown + 1) {
|
for i in first_shown..(last_shown + 1) {
|
||||||
let icon = if i == self.active_page {
|
let icon = if i == self.active_page {
|
||||||
theme::DOT_ACTIVE
|
Icon::new(theme::DOT_ACTIVE)
|
||||||
} else if i <= first_shown + 1 {
|
} else if i <= first_shown + 1 {
|
||||||
let before_first_shown = first_shown;
|
let before_first_shown = first_shown;
|
||||||
dotsize(i - first_shown, before_first_shown)
|
dotsize(i - first_shown, before_first_shown)
|
||||||
@ -108,9 +108,9 @@ impl Component for ScrollBar {
|
|||||||
let after_last_shown = self.page_count - 1 - last_shown;
|
let after_last_shown = self.page_count - 1 - last_shown;
|
||||||
dotsize(last_shown - i, after_last_shown)
|
dotsize(last_shown - i, after_last_shown)
|
||||||
} else {
|
} else {
|
||||||
theme::DOT_INACTIVE
|
Icon::new(theme::DOT_INACTIVE)
|
||||||
};
|
};
|
||||||
display::icon(cursor, icon, theme::FG, theme::BG);
|
icon.draw(cursor, CENTER, theme::FG, theme::BG);
|
||||||
cursor = cursor + Offset::on_axis(self.layout.axis, Self::DOT_INTERVAL);
|
cursor = cursor + Offset::on_axis(self.layout.axis, Self::DOT_INTERVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
Border, Component, Empty, Timeout, TimeoutMsg,
|
Border, Component, Empty, Timeout, TimeoutMsg,
|
||||||
},
|
},
|
||||||
display::tjpgd::jpeg_info,
|
display::{tjpgd::jpeg_info, toif::Icon},
|
||||||
geometry,
|
geometry,
|
||||||
layout::{
|
layout::{
|
||||||
obj::{ComponentMsgObj, LayoutObj},
|
obj::{ComponentMsgObj, LayoutObj},
|
||||||
@ -615,7 +615,7 @@ extern "C" fn new_confirm_modify_output(n_args: usize, args: *const Obj, kwargs:
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let buttons = Button::cancel_confirm(
|
let buttons = Button::cancel_confirm(
|
||||||
Button::with_icon(theme::ICON_CANCEL),
|
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
@ -650,7 +650,7 @@ extern "C" fn new_confirm_modify_fee(n_args: usize, args: *const Obj, kwargs: *m
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let buttons = Button::cancel_confirm(
|
let buttons = Button::cancel_confirm(
|
||||||
Button::with_icon(theme::ICON_CANCEL),
|
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
@ -701,7 +701,7 @@ fn new_show_modal(
|
|||||||
icon,
|
icon,
|
||||||
title,
|
title,
|
||||||
Button::cancel_confirm(
|
Button::cancel_confirm(
|
||||||
Button::with_icon(theme::ICON_CANCEL).styled(theme::button_cancel()),
|
Button::with_icon(Icon::new(theme::ICON_CANCEL)).styled(theme::button_cancel()),
|
||||||
Button::with_text(button).styled(button_style),
|
Button::with_text(button).styled(button_style),
|
||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
@ -730,8 +730,8 @@ fn new_show_modal(
|
|||||||
extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let icon = BlendedImage::new(
|
let icon = BlendedImage::new(
|
||||||
theme::IMAGE_BG_CIRCLE,
|
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||||
theme::IMAGE_FG_ERROR,
|
Icon::new(theme::IMAGE_FG_ERROR),
|
||||||
theme::ERROR_COLOR,
|
theme::ERROR_COLOR,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
@ -759,7 +759,7 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
};
|
};
|
||||||
|
|
||||||
let controls = Button::cancel_confirm(
|
let controls = Button::cancel_confirm(
|
||||||
Button::with_icon(theme::ICON_CANCEL),
|
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||||
Button::with_text("CONFIRM").styled(theme::button_confirm()),
|
Button::with_text("CONFIRM").styled(theme::button_confirm()),
|
||||||
2,
|
2,
|
||||||
);
|
);
|
||||||
@ -777,8 +777,8 @@ extern "C" fn new_confirm_fido(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let icon = BlendedImage::new(
|
let icon = BlendedImage::new(
|
||||||
theme::IMAGE_BG_TRIANGLE,
|
Icon::new(theme::IMAGE_BG_TRIANGLE),
|
||||||
theme::IMAGE_FG_WARN,
|
Icon::new(theme::IMAGE_FG_WARN),
|
||||||
theme::WARN_COLOR,
|
theme::WARN_COLOR,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
@ -791,8 +791,8 @@ extern "C" fn new_show_warning(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let icon = BlendedImage::new(
|
let icon = BlendedImage::new(
|
||||||
theme::IMAGE_BG_CIRCLE,
|
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||||
theme::IMAGE_FG_SUCCESS,
|
Icon::new(theme::IMAGE_FG_SUCCESS),
|
||||||
theme::SUCCESS_COLOR,
|
theme::SUCCESS_COLOR,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
@ -805,8 +805,8 @@ extern "C" fn new_show_success(n_args: usize, args: *const Obj, kwargs: *mut Map
|
|||||||
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
extern "C" fn new_show_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
|
||||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||||
let icon = BlendedImage::new(
|
let icon = BlendedImage::new(
|
||||||
theme::IMAGE_BG_CIRCLE,
|
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||||
theme::IMAGE_FG_INFO,
|
Icon::new(theme::IMAGE_FG_INFO),
|
||||||
theme::INFO_COLOR,
|
theme::INFO_COLOR,
|
||||||
theme::FG,
|
theme::FG,
|
||||||
theme::BG,
|
theme::BG,
|
||||||
@ -1088,8 +1088,8 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
|
|||||||
title,
|
title,
|
||||||
Dialog::new(
|
Dialog::new(
|
||||||
Checklist::from_paragraphs(
|
Checklist::from_paragraphs(
|
||||||
theme::ICON_LIST_CURRENT,
|
Icon::new(theme::ICON_LIST_CURRENT),
|
||||||
theme::ICON_LIST_CHECK,
|
Icon::new(theme::ICON_LIST_CHECK),
|
||||||
active,
|
active,
|
||||||
paragraphs
|
paragraphs
|
||||||
.into_paragraphs()
|
.into_paragraphs()
|
||||||
@ -1141,13 +1141,13 @@ extern "C" fn new_confirm_recovery(n_args: usize, args: *const Obj, kwargs: *mut
|
|||||||
|
|
||||||
let obj = if info_button {
|
let obj = if info_button {
|
||||||
LayoutObj::new(NotificationFrame::new(
|
LayoutObj::new(NotificationFrame::new(
|
||||||
theme::ICON_WARN,
|
Icon::new(theme::ICON_WARN),
|
||||||
notification,
|
notification,
|
||||||
Dialog::new(paragraphs, Button::<&'static str>::abort_info_enter()),
|
Dialog::new(paragraphs, Button::<&'static str>::abort_info_enter()),
|
||||||
))?
|
))?
|
||||||
} else {
|
} else {
|
||||||
LayoutObj::new(NotificationFrame::new(
|
LayoutObj::new(NotificationFrame::new(
|
||||||
theme::ICON_WARN,
|
Icon::new(theme::ICON_WARN),
|
||||||
notification,
|
notification,
|
||||||
Dialog::new(paragraphs, Button::cancel_confirm_text(None, button)),
|
Dialog::new(paragraphs, Button::cancel_confirm_text(None, button)),
|
||||||
))?
|
))?
|
||||||
|
@ -12,6 +12,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::component::{ButtonStyle, ButtonStyleSheet, LoaderStyle, LoaderStyleSheet};
|
use super::component::{ButtonStyle, ButtonStyleSheet, LoaderStyle, LoaderStyleSheet};
|
||||||
|
|
||||||
|
use crate::ui::display::toif::NamedToif;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
pub const ERASE_HOLD_DURATION: Duration = Duration::from_millis(1500);
|
pub const ERASE_HOLD_DURATION: Duration = Duration::from_millis(1500);
|
||||||
@ -52,41 +53,67 @@ pub const QR_SIDE_MAX: u32 = 140;
|
|||||||
pub const ICON_SIZE: i16 = 16;
|
pub const ICON_SIZE: i16 = 16;
|
||||||
|
|
||||||
// UI icons (greyscale).
|
// UI icons (greyscale).
|
||||||
pub const ICON_CANCEL: &[u8] = include_res!("model_tt/res/cancel.toif");
|
pub const ICON_CANCEL: NamedToif = NamedToif(include_res!("model_tt/res/cancel.toif"), "cancel");
|
||||||
pub const ICON_CONFIRM: &[u8] = include_res!("model_tt/res/confirm.toif");
|
pub const ICON_CONFIRM: NamedToif = NamedToif(include_res!("model_tt/res/confirm.toif"), "confirm");
|
||||||
pub const ICON_SPACE: &[u8] = include_res!("model_tt/res/space.toif");
|
pub const ICON_SPACE: NamedToif = NamedToif(include_res!("model_tt/res/space.toif"), "space");
|
||||||
pub const ICON_BACK: &[u8] = include_res!("model_tt/res/back.toif");
|
pub const ICON_BACK: NamedToif = NamedToif(include_res!("model_tt/res/back.toif"), "back");
|
||||||
pub const ICON_CLICK: &[u8] = include_res!("model_tt/res/click.toif");
|
pub const ICON_CLICK: NamedToif = NamedToif(include_res!("model_tt/res/click.toif"), "click");
|
||||||
pub const ICON_NEXT: &[u8] = include_res!("model_tt/res/next.toif");
|
pub const ICON_NEXT: NamedToif = NamedToif(include_res!("model_tt/res/next.toif"), "next");
|
||||||
pub const ICON_WARN: &[u8] = include_res!("model_tt/res/warn-icon.toif");
|
pub const ICON_WARN: NamedToif = NamedToif(include_res!("model_tt/res/warn-icon.toif"), "warn");
|
||||||
pub const ICON_MAGIC: &[u8] = include_res!("model_tt/res/magic.toif");
|
pub const ICON_MAGIC: NamedToif = NamedToif(include_res!("model_tt/res/magic.toif"), "magic");
|
||||||
pub const ICON_LIST_CURRENT: &[u8] = include_res!("model_tt/res/current.toif");
|
pub const ICON_LIST_CURRENT: NamedToif =
|
||||||
pub const ICON_LIST_CHECK: &[u8] = include_res!("model_tt/res/check.toif");
|
NamedToif(include_res!("model_tt/res/current.toif"), "current");
|
||||||
pub const ICON_LOCK: &[u8] = include_res!("model_tt/res/lock.toif");
|
pub const ICON_LIST_CHECK: NamedToif = NamedToif(include_res!("model_tt/res/check.toif"), "check");
|
||||||
|
pub const ICON_LOCK: NamedToif = NamedToif(include_res!("model_tt/res/lock.toif"), "lock");
|
||||||
|
|
||||||
// Large, three-color icons.
|
// Large, three-color icons.
|
||||||
pub const WARN_COLOR: Color = YELLOW;
|
pub const WARN_COLOR: Color = YELLOW;
|
||||||
pub const INFO_COLOR: Color = BLUE;
|
pub const INFO_COLOR: Color = BLUE;
|
||||||
pub const SUCCESS_COLOR: Color = GREEN;
|
pub const SUCCESS_COLOR: Color = GREEN;
|
||||||
pub const ERROR_COLOR: Color = RED;
|
pub const ERROR_COLOR: Color = RED;
|
||||||
pub const IMAGE_FG_WARN: &[u8] = include_res!("model_tt/res/warn_fg.toif");
|
pub const IMAGE_FG_WARN: NamedToif =
|
||||||
pub const IMAGE_FG_SUCCESS: &[u8] = include_res!("model_tt/res/success_fg.toif");
|
NamedToif(include_res!("model_tt/res/warn_fg.toif"), "warn_fg");
|
||||||
pub const IMAGE_FG_ERROR: &[u8] = include_res!("model_tt/res/error_fg.toif");
|
pub const IMAGE_FG_SUCCESS: NamedToif =
|
||||||
pub const IMAGE_FG_INFO: &[u8] = include_res!("model_tt/res/info_fg.toif");
|
NamedToif(include_res!("model_tt/res/success_fg.toif"), "success_fg");
|
||||||
pub const IMAGE_BG_CIRCLE: &[u8] = include_res!("model_tt/res/circle.toif");
|
pub const IMAGE_FG_ERROR: NamedToif =
|
||||||
pub const IMAGE_BG_TRIANGLE: &[u8] = include_res!("model_tt/res/triangle.toif");
|
NamedToif(include_res!("model_tt/res/error_fg.toif"), "error_fg");
|
||||||
pub const IMAGE_BG_BACK_BTN: &[u8] = include_res!("model_tt/res/back_btn.toif");
|
pub const IMAGE_FG_INFO: NamedToif =
|
||||||
pub const IMAGE_BG_BACK_BTN_TALL: &[u8] = include_res!("model_tt/res/back_btn_tall.toif");
|
NamedToif(include_res!("model_tt/res/info_fg.toif"), "info_fg");
|
||||||
|
pub const IMAGE_BG_CIRCLE: NamedToif =
|
||||||
|
NamedToif(include_res!("model_tt/res/circle.toif"), "circle");
|
||||||
|
pub const IMAGE_BG_TRIANGLE: NamedToif =
|
||||||
|
NamedToif(include_res!("model_tt/res/triangle.toif"), "triangle");
|
||||||
|
pub const IMAGE_BG_BACK_BTN: NamedToif =
|
||||||
|
NamedToif(include_res!("model_tt/res/back_btn.toif"), "back_btn");
|
||||||
|
pub const IMAGE_BG_BACK_BTN_TALL: NamedToif = NamedToif(
|
||||||
|
include_res!("model_tt/res/back_btn_tall.toif"),
|
||||||
|
"back_btn_tall",
|
||||||
|
);
|
||||||
|
|
||||||
// Default homescreen
|
// Default homescreen
|
||||||
pub const IMAGE_HOMESCREEN: &[u8] = include_res!("model_tt/res/bg.jpg");
|
pub const IMAGE_HOMESCREEN: &[u8] = include_res!("model_tt/res/bg.jpg");
|
||||||
|
|
||||||
// Scrollbar/PIN dots.
|
// Scrollbar/PIN dots.
|
||||||
pub const DOT_ACTIVE: &[u8] = include_res!("model_tt/res/scroll-active.toif");
|
pub const DOT_ACTIVE: NamedToif = NamedToif(
|
||||||
pub const DOT_INACTIVE: &[u8] = include_res!("model_tt/res/scroll-inactive.toif");
|
include_res!("model_tt/res/scroll-active.toif"),
|
||||||
pub const DOT_INACTIVE_HALF: &[u8] = include_res!("model_tt/res/scroll-inactive-half.toif");
|
"scroll-active",
|
||||||
pub const DOT_INACTIVE_QUARTER: &[u8] = include_res!("model_tt/res/scroll-inactive-quarter.toif");
|
);
|
||||||
pub const DOT_SMALL: &[u8] = include_res!("model_tt/res/scroll-small.toif");
|
pub const DOT_INACTIVE: NamedToif = NamedToif(
|
||||||
|
include_res!("model_tt/res/scroll-inactive.toif"),
|
||||||
|
"scroll-inactive",
|
||||||
|
);
|
||||||
|
pub const DOT_INACTIVE_HALF: NamedToif = NamedToif(
|
||||||
|
include_res!("model_tt/res/scroll-inactive-half.toif"),
|
||||||
|
"scroll-inactive-half",
|
||||||
|
);
|
||||||
|
pub const DOT_INACTIVE_QUARTER: NamedToif = NamedToif(
|
||||||
|
include_res!("model_tt/res/scroll-inactive-quarter.toif"),
|
||||||
|
"scroll-inactive-quarter",
|
||||||
|
);
|
||||||
|
pub const DOT_SMALL: NamedToif = NamedToif(
|
||||||
|
include_res!("model_tt/res/scroll-small.toif"),
|
||||||
|
"scroll-small",
|
||||||
|
);
|
||||||
|
|
||||||
pub const fn label_default() -> TextStyle {
|
pub const fn label_default() -> TextStyle {
|
||||||
TEXT_NORMAL
|
TEXT_NORMAL
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::text::TextStyle,
|
component::text::TextStyle,
|
||||||
display,
|
display,
|
||||||
geometry::{Offset, Point},
|
display::toif::Icon,
|
||||||
|
geometry::{Offset, Point, CENTER},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ResultExt {
|
pub trait ResultExt {
|
||||||
@ -64,14 +65,13 @@ pub fn set_animation_disabled(_disabled: bool) {}
|
|||||||
/// Display an icon and a text centered relative to given `Point`.
|
/// Display an icon and a text centered relative to given `Point`.
|
||||||
pub fn icon_text_center(
|
pub fn icon_text_center(
|
||||||
baseline: Point,
|
baseline: Point,
|
||||||
icon: &'static [u8],
|
icon: Icon,
|
||||||
space: i16,
|
space: i16,
|
||||||
text: &str,
|
text: &str,
|
||||||
style: TextStyle,
|
style: TextStyle,
|
||||||
text_offset: Offset,
|
text_offset: Offset,
|
||||||
) {
|
) {
|
||||||
let toif_info = unwrap!(display::toif_info(icon), "Invalid TOIF data");
|
let icon_width = icon.toif.width();
|
||||||
let icon_width = toif_info.0.y;
|
|
||||||
let text_width = style.text_font.text_width(text);
|
let text_width = style.text_font.text_width(text);
|
||||||
let text_height = style.text_font.text_height();
|
let text_height = style.text_font.text_height();
|
||||||
let text_center = baseline + Offset::new((icon_width + space) / 2, text_height / 2);
|
let text_center = baseline + Offset::new((icon_width + space) / 2, text_height / 2);
|
||||||
@ -84,7 +84,12 @@ pub fn icon_text_center(
|
|||||||
style.text_color,
|
style.text_color,
|
||||||
style.background_color,
|
style.background_color,
|
||||||
);
|
);
|
||||||
display::icon(icon_center, icon, style.text_color, style.background_color);
|
icon.draw(
|
||||||
|
icon_center,
|
||||||
|
CENTER,
|
||||||
|
style.text_color,
|
||||||
|
style.background_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user