mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-03 08:46:05 +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_radius")
|
||||
.allowlist_function("display_bar_radius_buffer")
|
||||
.allowlist_function("display_icon")
|
||||
.allowlist_function("display_image")
|
||||
.allowlist_function("display_toif_info")
|
||||
.allowlist_function("display_loader")
|
||||
@ -321,8 +320,10 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("hal_delay")
|
||||
.allowlist_function("hal_ticks_ms")
|
||||
// dma2d
|
||||
.allowlist_function("dma2d_setup_4bpp")
|
||||
.allowlist_function("dma2d_setup_4bpp_over_4bpp")
|
||||
.allowlist_function("dma2d_setup_4bpp_over_16bpp")
|
||||
.allowlist_function("dma2d_start")
|
||||
.allowlist_function("dma2d_start_blend")
|
||||
.allowlist_function("dma2d_wait_for_transfer")
|
||||
//buffers
|
||||
|
@ -5,7 +5,7 @@ use num_traits::FromPrimitive;
|
||||
|
||||
use crate::trezorhal::buffers::BufferText;
|
||||
|
||||
#[derive(PartialEq, Debug, Eq, FromPrimitive)]
|
||||
#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
|
||||
pub enum ToifFormat {
|
||||
FullColorBE = ffi::toif_format_t_TOIF_FULL_COLOR_BE 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]) {
|
||||
unsafe {
|
||||
ffi::display_image(
|
||||
|
@ -1,5 +1,9 @@
|
||||
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) {
|
||||
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) }
|
||||
}
|
||||
|
||||
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) {
|
||||
unsafe {
|
||||
ffi::dma2d_start_blend(
|
||||
|
@ -1,22 +1,38 @@
|
||||
use crate::ui::{
|
||||
use crate::{
|
||||
trezorhal::display::{image, ToifFormat},
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display,
|
||||
display::{toif_info, Color},
|
||||
geometry::{Alignment, Offset, Point, Rect},
|
||||
display::{
|
||||
toif::{NamedToif, Toif},
|
||||
Color, Icon,
|
||||
},
|
||||
geometry::{Alignment2D, Offset, Point, Rect, CENTER},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Image {
|
||||
image: &'static [u8],
|
||||
pub toif: Toif,
|
||||
area: Rect,
|
||||
}
|
||||
|
||||
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 {
|
||||
image,
|
||||
toif,
|
||||
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 {
|
||||
@ -32,13 +48,14 @@ impl Component for Image {
|
||||
}
|
||||
|
||||
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)) {
|
||||
if let Some((size, _)) = display::toif_info(self.image) {
|
||||
sink(Rect::from_center_and_size(self.area.center(), size));
|
||||
}
|
||||
sink(Rect::from_center_and_size(
|
||||
self.area.center(),
|
||||
self.toif.size,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,8 +68,8 @@ impl crate::trace::Trace for Image {
|
||||
}
|
||||
|
||||
pub struct BlendedImage {
|
||||
bg: &'static [u8],
|
||||
fg: &'static [u8],
|
||||
bg: Icon,
|
||||
fg: Icon,
|
||||
bg_color: Color,
|
||||
fg_color: Color,
|
||||
area_color: Color,
|
||||
@ -61,13 +78,7 @@ pub struct BlendedImage {
|
||||
}
|
||||
|
||||
impl BlendedImage {
|
||||
pub fn new(
|
||||
bg: &'static [u8],
|
||||
fg: &'static [u8],
|
||||
bg_color: Color,
|
||||
fg_color: Color,
|
||||
area_color: Color,
|
||||
) -> Self {
|
||||
pub fn new(bg: Icon, fg: Icon, bg_color: Color, fg_color: Color, area_color: Color) -> Self {
|
||||
Self {
|
||||
bg,
|
||||
fg,
|
||||
@ -93,15 +104,12 @@ impl Component for BlendedImage {
|
||||
type Msg = Never;
|
||||
|
||||
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);
|
||||
|
||||
if let Some((fg_size, _)) = toif_info(self.fg) {
|
||||
let ft_top_left = fg_size.snap(bounds.center(), Alignment::Center, Alignment::Center);
|
||||
let ft_top_left = self.fg.toif.size.snap(bounds.center(), CENTER);
|
||||
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> {
|
||||
@ -113,9 +121,10 @@ impl Component for BlendedImage {
|
||||
}
|
||||
|
||||
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
||||
if let Some((size, _)) = display::toif_info(self.bg) {
|
||||
sink(Rect::from_top_left_and_size(self.bg_top_left, size));
|
||||
}
|
||||
sink(Rect::from_top_left_and_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 border::Border;
|
||||
pub use empty::Empty;
|
||||
pub use image::Image;
|
||||
pub use label::Label;
|
||||
pub use map::Map;
|
||||
pub use maybe::Maybe;
|
||||
|
@ -1,9 +1,9 @@
|
||||
#[cfg(feature = "jpeg")]
|
||||
use crate::ui::geometry::Offset;
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
component::{image::Image, Component, Event, EventCtx, Never},
|
||||
display,
|
||||
geometry::Rect,
|
||||
geometry::{Rect, CENTER},
|
||||
};
|
||||
|
||||
pub struct Painter<F> {
|
||||
@ -62,8 +62,8 @@ where
|
||||
Painter::new(f)
|
||||
}
|
||||
|
||||
pub fn image_painter(image: &'static [u8]) -> Painter<impl FnMut(Rect)> {
|
||||
let f = move |area: Rect| display::image(area.center(), image);
|
||||
pub fn image_painter(image: Image) -> Painter<impl FnMut(Rect)> {
|
||||
let f = move |area: Rect| image.draw(area.center(), CENTER);
|
||||
Painter::new(f)
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@ use heapless::Vec;
|
||||
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never, Paginate},
|
||||
display,
|
||||
geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect},
|
||||
display::toif::Icon,
|
||||
geometry::{Alignment, Insets, LinearPlacement, Offset, Point, Rect, TOP_LEFT},
|
||||
};
|
||||
|
||||
use super::layout::{LayoutFit, TextLayout, TextStyle};
|
||||
@ -505,8 +505,8 @@ pub struct Checklist<T> {
|
||||
area: Rect,
|
||||
paragraphs: Paragraphs<T>,
|
||||
current: usize,
|
||||
icon_current: &'static [u8],
|
||||
icon_done: &'static [u8],
|
||||
icon_current: Icon,
|
||||
icon_done: Icon,
|
||||
}
|
||||
|
||||
impl<T> Checklist<T> {
|
||||
@ -515,8 +515,8 @@ impl<T> Checklist<T> {
|
||||
const CURRENT_OFFSET: Offset = Offset::new(2, 3);
|
||||
|
||||
pub fn from_paragraphs(
|
||||
icon_current: &'static [u8],
|
||||
icon_done: &'static [u8],
|
||||
icon_current: Icon,
|
||||
icon_done: Icon,
|
||||
current: usize,
|
||||
paragraphs: Paragraphs<T>,
|
||||
) -> 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);
|
||||
display::icon_top_left(
|
||||
icon.draw(
|
||||
top_left + offset,
|
||||
icon,
|
||||
TOP_LEFT,
|
||||
layout.style.text_color,
|
||||
layout.style.background_color,
|
||||
);
|
||||
|
@ -1,10 +1,7 @@
|
||||
use crate::{
|
||||
trezorhal::uzlib::UzlibContext,
|
||||
ui::{
|
||||
use crate::ui::{
|
||||
constant, display,
|
||||
display::{Color, ToifFormat},
|
||||
display::Color,
|
||||
geometry::{Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
use core::slice::from_raw_parts;
|
||||
|
||||
@ -16,7 +13,7 @@ use crate::trezorhal::{
|
||||
|
||||
use crate::ui::{
|
||||
constant::{screen, LOADER_OUTER},
|
||||
display::toif_info_ensure,
|
||||
display::toif::{Icon, NamedToif},
|
||||
};
|
||||
|
||||
pub const LOADER_MIN: u16 = 0;
|
||||
@ -41,17 +38,15 @@ pub fn loader_uncompress(
|
||||
bg_color: Color,
|
||||
progress: u16,
|
||||
indeterminate: bool,
|
||||
icon: Option<(&[u8], Color)>,
|
||||
icon: Option<(Icon, Color)>,
|
||||
) {
|
||||
const ICON_MAX_SIZE: i16 = constant::LOADER_ICON_MAX_SIZE;
|
||||
|
||||
if let Some((data, color)) = icon {
|
||||
let (toif_size, toif_data) = toif_info_ensure(data, ToifFormat::GrayScaleEH);
|
||||
if toif_size.x <= ICON_MAX_SIZE && toif_size.y <= ICON_MAX_SIZE {
|
||||
if let Some((icon, color)) = icon {
|
||||
if icon.toif.width() <= ICON_MAX_SIZE && icon.toif.height() <= ICON_MAX_SIZE {
|
||||
let mut icon_data = [0_u8; ((ICON_MAX_SIZE * ICON_MAX_SIZE) / 2) as usize];
|
||||
let mut ctx = UzlibContext::new(toif_data, None);
|
||||
unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed");
|
||||
let i = Some((icon_data.as_ref(), color, toif_size));
|
||||
icon.toif.uncompress(&mut icon_data);
|
||||
let i = Some((icon, color, icon.toif.size));
|
||||
loader_rust(y_offset, fg_color, bg_color, progress, indeterminate, i);
|
||||
} else {
|
||||
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 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 {
|
||||
None
|
||||
};
|
||||
@ -193,7 +188,7 @@ pub fn loader_rust(
|
||||
bg_color: Color,
|
||||
progress: u16,
|
||||
indeterminate: bool,
|
||||
icon: Option<(&[u8], Color, Offset)>,
|
||||
icon: Option<(Icon, Color, 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));
|
||||
@ -209,14 +204,14 @@ pub fn loader_rust(
|
||||
let mut icon_area = Rect::zero();
|
||||
let mut icon_area_clamped = Rect::zero();
|
||||
let mut icon_width = 0;
|
||||
let mut icon_data = [].as_ref();
|
||||
let mut icon_data = None;
|
||||
|
||||
if let Some((data, color, size)) = icon {
|
||||
if size.x <= ICON_MAX_SIZE && size.y <= ICON_MAX_SIZE {
|
||||
icon_width = size.x;
|
||||
icon_area = Rect::from_center_and_size(center, size);
|
||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||
icon_data = data;
|
||||
icon_data = Some(data);
|
||||
use_icon = true;
|
||||
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 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 {
|
||||
underlying_color = icon_colortable[(data & 0xF) as usize];
|
||||
} else {
|
||||
@ -273,7 +269,7 @@ pub fn loader_rust(
|
||||
bg_color: Color,
|
||||
progress: u16,
|
||||
indeterminate: bool,
|
||||
icon: Option<(&[u8], Color, Offset)>,
|
||||
icon: Option<(Icon, Color, 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));
|
||||
@ -288,16 +284,16 @@ pub fn loader_rust(
|
||||
let mut icon_width = 0;
|
||||
let mut icon_offset = 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 {
|
||||
icon_width = size.x;
|
||||
icon_area = Rect::from_center_and_size(center, size);
|
||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||
icon_offset = (icon_area_clamped.x0 - r.x0) / 2;
|
||||
icon_color = color;
|
||||
icon_data = data;
|
||||
icon_data = Some(icon);
|
||||
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]
|
||||
.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],
|
||||
);
|
||||
icon_buffer = icon_buffer_used;
|
||||
@ -380,7 +376,7 @@ pub fn loader(
|
||||
y_offset: i16,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Option<(&[u8], Color)>,
|
||||
icon: Option<(Icon, Color)>,
|
||||
) {
|
||||
loader_uncompress(y_offset, fg_color, bg_color, progress, false, icon);
|
||||
}
|
||||
@ -390,7 +386,7 @@ pub fn loader_indeterminate(
|
||||
y_offset: i16,
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
icon: Option<(&[u8], Color)>,
|
||||
icon: Option<(Icon, Color)>,
|
||||
) {
|
||||
loader_uncompress(y_offset, fg_color, bg_color, progress, true, icon);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
pub mod loader;
|
||||
#[cfg(feature = "jpeg")]
|
||||
pub mod tjpgd;
|
||||
pub mod toif;
|
||||
|
||||
use super::{
|
||||
constant,
|
||||
@ -14,20 +15,21 @@ use crate::trezorhal::{
|
||||
dma2d_setup_4bpp_over_16bpp, dma2d_setup_4bpp_over_4bpp, dma2d_start_blend,
|
||||
dma2d_wait_for_transfer,
|
||||
},
|
||||
uzlib::UZLIB_WINDOW_SIZE,
|
||||
};
|
||||
#[cfg(not(feature = "dma2d"))]
|
||||
use crate::ui::geometry::TOP_LEFT;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
time::Duration,
|
||||
trezorhal::{
|
||||
display,
|
||||
display::ToifFormat,
|
||||
qr, time,
|
||||
uzlib::{UzlibContext, UZLIB_WINDOW_SIZE},
|
||||
},
|
||||
trezorhal::{display, qr, time, uzlib::UzlibContext},
|
||||
ui::lerp::Lerp,
|
||||
};
|
||||
use core::slice;
|
||||
|
||||
use crate::ui::component::image::Image;
|
||||
pub use crate::ui::display::toif::Icon;
|
||||
#[cfg(any(feature = "model_tt", feature = "model_tr"))]
|
||||
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.
|
||||
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());
|
||||
@ -306,7 +208,7 @@ pub fn rect_rounded2_partial(
|
||||
fg_color: Color,
|
||||
bg_color: Color,
|
||||
show_percent: i16,
|
||||
icon: Option<(&[u8], Color)>,
|
||||
icon: Option<(Icon, Color)>,
|
||||
) {
|
||||
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_width = 0;
|
||||
|
||||
if let Some((icon_bytes, icon_color)) = icon {
|
||||
let (toif_size, toif_data) = toif_info_ensure(icon_bytes, ToifFormat::GrayScaleEH);
|
||||
|
||||
if toif_size.x <= MAX_ICON_SIZE && toif_size.y <= MAX_ICON_SIZE {
|
||||
icon_area = Rect::from_center_and_size(center, toif_size);
|
||||
if let Some((icon, icon_color)) = icon {
|
||||
if icon.toif.width() <= MAX_ICON_SIZE && icon.toif.height() <= MAX_ICON_SIZE {
|
||||
icon_area = Rect::from_center_and_size(center, icon.toif.size);
|
||||
icon_area_clamped = icon_area.clamp(constant::screen());
|
||||
|
||||
let mut ctx = UzlibContext::new(toif_data, None);
|
||||
unwrap!(ctx.uncompress(&mut icon_data), "Decompression failed");
|
||||
icon.toif.uncompress(&mut icon_data);
|
||||
icon_colortable = get_color_table(icon_color, bg_color);
|
||||
icon_width = toif_size.x;
|
||||
icon_width = icon.toif.width();
|
||||
use_icon = true;
|
||||
}
|
||||
}
|
||||
@ -533,7 +431,7 @@ fn process_buffer(
|
||||
#[cfg(feature = "dma2d")]
|
||||
pub fn text_over_image(
|
||||
bg_area: Option<(Rect, Color)>,
|
||||
image_data: &[u8],
|
||||
image: Image,
|
||||
text: &str,
|
||||
font: Font,
|
||||
offset_img: Offset,
|
||||
@ -548,8 +446,6 @@ pub fn text_over_image(
|
||||
let t2 = unsafe { get_buffer_4bpp(1, 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 area;
|
||||
let offset_img_final;
|
||||
@ -565,10 +461,10 @@ pub fn text_over_image(
|
||||
empty_img.buffer.copy_from_slice(&img1.buffer);
|
||||
|
||||
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;
|
||||
} 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;
|
||||
offset_img_final = Offset::zero();
|
||||
}
|
||||
@ -594,7 +490,7 @@ pub fn text_over_image(
|
||||
set_window(clamped);
|
||||
|
||||
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());
|
||||
|
||||
@ -662,8 +558,8 @@ pub fn text_over_image(
|
||||
#[cfg(feature = "dma2d")]
|
||||
pub fn icon_over_icon(
|
||||
bg_area: Option<Rect>,
|
||||
bg: (&[u8], Offset, Color),
|
||||
fg: (&[u8], Offset, Color),
|
||||
bg: (Icon, Offset, Color),
|
||||
fg: (Icon, Offset, Color),
|
||||
bg_color: Color,
|
||||
) {
|
||||
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 empty2 = unsafe { get_buffer_4bpp(2, true) };
|
||||
|
||||
let (data_bg, offset_bg, color_icon_bg) = bg;
|
||||
let (data_fg, offset_fg, color_icon_fg) = fg;
|
||||
let (icon_bg, offset_bg, color_icon_bg) = bg;
|
||||
let (icon_fg, offset_fg, color_icon_fg) = fg;
|
||||
|
||||
let (toif_bg_size, toif_bg_data) = toif_info_ensure(data_bg, ToifFormat::GrayScaleEH);
|
||||
assert!(toif_bg_size.x <= constant::WIDTH);
|
||||
assert_eq!(toif_bg_size.x % 2, 0);
|
||||
assert!(icon_bg.toif.width() <= constant::WIDTH);
|
||||
assert_eq!(icon_bg.toif.width() % 2, 0);
|
||||
|
||||
let (toif_fg_size, toif_fg_data) = toif_info_ensure(data_fg, ToifFormat::GrayScaleEH);
|
||||
assert!(toif_bg_size.x <= constant::WIDTH);
|
||||
assert_eq!(toif_bg_size.x % 2, 0);
|
||||
assert!(icon_fg.toif.width() <= constant::WIDTH);
|
||||
assert_eq!(icon_fg.toif.width() % 2, 0);
|
||||
|
||||
let area;
|
||||
let r_bg;
|
||||
let final_offset_bg;
|
||||
if let Some(a) = bg_area {
|
||||
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;
|
||||
} 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;
|
||||
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();
|
||||
|
||||
set_window(clamped);
|
||||
|
||||
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 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());
|
||||
|
||||
@ -766,12 +661,12 @@ pub fn icon_over_icon(
|
||||
#[cfg(not(feature = "dma2d"))]
|
||||
pub fn icon_over_icon(
|
||||
bg_area: Option<Rect>,
|
||||
bg: (&[u8], Offset, Color),
|
||||
fg: (&[u8], Offset, Color),
|
||||
bg: (Icon, Offset, Color),
|
||||
fg: (Icon, Offset, Color),
|
||||
bg_color: Color,
|
||||
) {
|
||||
let (data_bg, offset_bg, color_icon_bg) = bg;
|
||||
let (data_fg, offset_fg, color_icon_fg) = fg;
|
||||
let (icon_bg, offset_bg, color_icon_bg) = bg;
|
||||
let (icon_fg, offset_fg, color_icon_fg) = fg;
|
||||
|
||||
let pos_bg = if let Some(area) = bg_area {
|
||||
rect_fill(area, bg_color);
|
||||
@ -780,8 +675,8 @@ pub fn icon_over_icon(
|
||||
Point::from(offset_bg)
|
||||
};
|
||||
|
||||
icon_top_left(pos_bg, data_bg, color_icon_bg, bg_color);
|
||||
icon_top_left(pos_bg + offset_fg, data_fg, color_icon_fg, color_icon_bg);
|
||||
icon_bg.draw(pos_bg, TOP_LEFT, color_icon_bg, bg_color);
|
||||
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
|
||||
|
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
|
||||
/// the rectangle such that it is aligned relative to the `point`.
|
||||
pub const fn snap(self, point: Point, x: Alignment, y: Alignment) -> Point {
|
||||
let x_off = match x {
|
||||
pub const fn snap(self, point: Point, alignment: Alignment2D) -> Point {
|
||||
let x_off = match alignment.0 {
|
||||
Alignment::Start => 0,
|
||||
Alignment::Center => self.x / 2,
|
||||
Alignment::End => self.x,
|
||||
};
|
||||
let y_off = match y {
|
||||
let y_off = match alignment.1 {
|
||||
Alignment::Start => 0,
|
||||
Alignment::Center => self.y / 2,
|
||||
Alignment::End => self.y,
|
||||
@ -224,6 +224,12 @@ impl Rect {
|
||||
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 {
|
||||
Self {
|
||||
x0: p0.x,
|
||||
@ -451,6 +457,14 @@ pub enum Alignment {
|
||||
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)]
|
||||
pub enum Axis {
|
||||
Horizontal,
|
||||
|
@ -4,6 +4,7 @@ use crate::{
|
||||
animation::Animation,
|
||||
component::{Component, Event, EventCtx},
|
||||
display,
|
||||
display::toif::Icon,
|
||||
geometry::Rect,
|
||||
model_tr::theme,
|
||||
},
|
||||
@ -23,11 +24,11 @@ pub struct ResultAnim {
|
||||
area: Rect,
|
||||
state: State,
|
||||
growing_duration: Duration,
|
||||
icon: &'static [u8],
|
||||
icon: Icon,
|
||||
}
|
||||
|
||||
impl ResultAnim {
|
||||
pub fn new(icon: &'static [u8]) -> Self {
|
||||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
area: Rect::zero(),
|
||||
state: State::Initial,
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
Child, Component, ComponentExt, Event, EventCtx, Label, Pad,
|
||||
},
|
||||
constant::screen,
|
||||
display::toif::Icon,
|
||||
geometry::{Alignment, Insets, LinearPlacement, Point, Rect},
|
||||
model_tr::{
|
||||
component::{Button, ButtonMsg, ButtonPos, ResultAnim, ResultAnimMsg},
|
||||
@ -38,7 +39,7 @@ const ANIM_POS_ADJ_BUTTON: i16 = 6;
|
||||
|
||||
impl<S: ParagraphStrType> ResultPopup<S> {
|
||||
pub fn new(
|
||||
icon: &'static [u8],
|
||||
icon: Icon,
|
||||
text: S,
|
||||
headline: Option<&'static str>,
|
||||
button_text: Option<&'static str>,
|
||||
|
@ -4,9 +4,9 @@ use crate::{
|
||||
component::{
|
||||
Component, ComponentExt, Event, EventCtx, FixedHeightBar, GridPlaced, Map, TimerToken,
|
||||
},
|
||||
display::{self, Color, Font},
|
||||
display::{self, toif::Icon, Color, Font},
|
||||
event::TouchEvent,
|
||||
geometry::{Insets, Offset, Rect},
|
||||
geometry::{Insets, Offset, Rect, CENTER},
|
||||
},
|
||||
};
|
||||
|
||||
@ -48,11 +48,11 @@ impl<T> Button<T> {
|
||||
Self::new(ButtonContent::Text(text))
|
||||
}
|
||||
|
||||
pub fn with_icon(image: &'static [u8]) -> Self {
|
||||
Self::new(ButtonContent::Icon(image))
|
||||
pub fn with_icon(icon: Icon) -> Self {
|
||||
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))
|
||||
}
|
||||
|
||||
@ -198,17 +198,17 @@ impl<T> Button<T> {
|
||||
);
|
||||
}
|
||||
ButtonContent::Icon(icon) => {
|
||||
display::icon(
|
||||
icon.draw(
|
||||
self.area.center(),
|
||||
icon,
|
||||
CENTER,
|
||||
style.text_color,
|
||||
style.button_color,
|
||||
);
|
||||
}
|
||||
ButtonContent::IconBlend(bg, fg, offset) => display::icon_over_icon(
|
||||
Some(self.area),
|
||||
(bg, Offset::zero(), style.button_color),
|
||||
(fg, *offset, style.text_color),
|
||||
(*bg, Offset::zero(), style.button_color),
|
||||
(*fg, *offset, style.text_color),
|
||||
style.background_color,
|
||||
),
|
||||
}
|
||||
@ -328,8 +328,8 @@ enum State {
|
||||
pub enum ButtonContent<T> {
|
||||
Empty,
|
||||
Text(T),
|
||||
Icon(&'static [u8]),
|
||||
IconBlend(&'static [u8], &'static [u8], Offset),
|
||||
Icon(Icon),
|
||||
IconBlend(Icon, Icon, Offset),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
@ -396,7 +396,7 @@ impl<T> Button<T> {
|
||||
let (left, right_size_factor) = if let Some(verb) = left {
|
||||
(Button::with_text(verb), 1)
|
||||
} 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());
|
||||
|
||||
@ -417,7 +417,7 @@ impl<T> Button<T> {
|
||||
{
|
||||
let right = Button::with_text(confirm).styled(theme::button_confirm());
|
||||
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(
|
||||
2,
|
||||
(
|
||||
|
@ -6,6 +6,7 @@ use crate::ui::{
|
||||
},
|
||||
Child, Component, Event, EventCtx, Never,
|
||||
},
|
||||
display::toif::Icon,
|
||||
geometry::{Insets, LinearPlacement, Rect},
|
||||
};
|
||||
|
||||
@ -128,8 +129,8 @@ where
|
||||
let [l0, l1, l2, l3] = lines;
|
||||
Self {
|
||||
image: Child::new(BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_SUCCESS,
|
||||
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||
Icon::new(theme::IMAGE_FG_SUCCESS),
|
||||
theme::SUCCESS_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ui::{
|
||||
component::{Child, Component, Event, EventCtx, Image, Label},
|
||||
component::{image::Image, Child, Component, Event, EventCtx, Label},
|
||||
display,
|
||||
geometry::{Alignment, Insets, Rect},
|
||||
model_tt::component::{
|
||||
|
@ -2,42 +2,45 @@
|
||||
//! (by running `make templates` in `core`)
|
||||
//! do not edit manually!
|
||||
|
||||
const ICON_AWS: &[u8] = include_res!("model_tt/res/fido/icon_aws.toif");
|
||||
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_BITWARDEN: &[u8] = include_res!("model_tt/res/fido/icon_bitwarden.toif");
|
||||
const ICON_CLOUDFLARE: &[u8] = include_res!("model_tt/res/fido/icon_cloudflare.toif");
|
||||
const ICON_COINBASE: &[u8] = include_res!("model_tt/res/fido/icon_coinbase.toif");
|
||||
const ICON_DASHLANE: &[u8] = include_res!("model_tt/res/fido/icon_dashlane.toif");
|
||||
const ICON_DROPBOX: &[u8] = include_res!("model_tt/res/fido/icon_dropbox.toif");
|
||||
const ICON_DUO: &[u8] = include_res!("model_tt/res/fido/icon_duo.toif");
|
||||
const ICON_FACEBOOK: &[u8] = include_res!("model_tt/res/fido/icon_facebook.toif");
|
||||
const ICON_FASTMAIL: &[u8] = include_res!("model_tt/res/fido/icon_fastmail.toif");
|
||||
const ICON_FEDORA: &[u8] = include_res!("model_tt/res/fido/icon_fedora.toif");
|
||||
const ICON_GANDI: &[u8] = include_res!("model_tt/res/fido/icon_gandi.toif");
|
||||
const ICON_GEMINI: &[u8] = include_res!("model_tt/res/fido/icon_gemini.toif");
|
||||
const ICON_GITHUB: &[u8] = include_res!("model_tt/res/fido/icon_github.toif");
|
||||
const ICON_GITLAB: &[u8] = include_res!("model_tt/res/fido/icon_gitlab.toif");
|
||||
const ICON_GOOGLE: &[u8] = include_res!("model_tt/res/fido/icon_google.toif");
|
||||
const ICON_INVITY: &[u8] = include_res!("model_tt/res/fido/icon_invity.toif");
|
||||
const ICON_KEEPER: &[u8] = include_res!("model_tt/res/fido/icon_keeper.toif");
|
||||
const ICON_KRAKEN: &[u8] = include_res!("model_tt/res/fido/icon_kraken.toif");
|
||||
const ICON_LOGIN_GOV: &[u8] = include_res!("model_tt/res/fido/icon_login.gov.toif");
|
||||
const ICON_MICROSOFT: &[u8] = include_res!("model_tt/res/fido/icon_microsoft.toif");
|
||||
const ICON_MOJEID: &[u8] = include_res!("model_tt/res/fido/icon_mojeid.toif");
|
||||
const ICON_NAMECHEAP: &[u8] = include_res!("model_tt/res/fido/icon_namecheap.toif");
|
||||
const ICON_PROTON: &[u8] = include_res!("model_tt/res/fido/icon_proton.toif");
|
||||
const ICON_SLUSHPOOL: &[u8] = include_res!("model_tt/res/fido/icon_slushpool.toif");
|
||||
const ICON_STRIPE: &[u8] = include_res!("model_tt/res/fido/icon_stripe.toif");
|
||||
const ICON_TUTANOTA: &[u8] = include_res!("model_tt/res/fido/icon_tutanota.toif");
|
||||
use crate::ui::display::toif::NamedToif;
|
||||
|
||||
|
||||
const ICON_AWS: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_aws.toif"), "AWS");
|
||||
const ICON_BINANCE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_binance.toif"), "BINANCE");
|
||||
const ICON_BITBUCKET: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitbucket.toif"), "BITBUCKET");
|
||||
const ICON_BITFINEX: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitfinex.toif"), "BITFINEX");
|
||||
const ICON_BITWARDEN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_bitwarden.toif"), "BITWARDEN");
|
||||
const ICON_CLOUDFLARE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_cloudflare.toif"), "CLOUDFLARE");
|
||||
const ICON_COINBASE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_coinbase.toif"), "COINBASE");
|
||||
const ICON_DASHLANE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_dashlane.toif"), "DASHLANE");
|
||||
const ICON_DROPBOX: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_dropbox.toif"), "DROPBOX");
|
||||
const ICON_DUO: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_duo.toif"), "DUO");
|
||||
const ICON_FACEBOOK: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_facebook.toif"), "FACEBOOK");
|
||||
const ICON_FASTMAIL: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_fastmail.toif"), "FASTMAIL");
|
||||
const ICON_FEDORA: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_fedora.toif"), "FEDORA");
|
||||
const ICON_GANDI: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gandi.toif"), "GANDI");
|
||||
const ICON_GEMINI: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gemini.toif"), "GEMINI");
|
||||
const ICON_GITHUB: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_github.toif"), "GITHUB");
|
||||
const ICON_GITLAB: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_gitlab.toif"), "GITLAB");
|
||||
const ICON_GOOGLE: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_google.toif"), "GOOGLE");
|
||||
const ICON_INVITY: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_invity.toif"), "INVITY");
|
||||
const ICON_KEEPER: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_keeper.toif"), "KEEPER");
|
||||
const ICON_KRAKEN: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_kraken.toif"), "KRAKEN");
|
||||
const ICON_LOGIN_GOV: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_login.gov.toif"), "LOGIN_GOV");
|
||||
const ICON_MICROSOFT: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_microsoft.toif"), "MICROSOFT");
|
||||
const ICON_MOJEID: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_mojeid.toif"), "MOJEID");
|
||||
const ICON_NAMECHEAP: NamedToif = NamedToif(include_res!("model_tt/res/fido/icon_namecheap.toif"), "NAMECHEAP");
|
||||
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
|
||||
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.
|
||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||
/// 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 {
|
||||
match icon_name.as_ref() {
|
||||
"aws" => ICON_AWS,
|
||||
|
@ -1,6 +1,9 @@
|
||||
//! generated from webauthn_icons.rs.mako
|
||||
//! (by running `make templates` in `core`)
|
||||
//! do not edit manually!
|
||||
|
||||
use crate::ui::display::toif::NamedToif;
|
||||
|
||||
<%
|
||||
icons: list[tuple[str, str]] = []
|
||||
for app in fido:
|
||||
@ -12,15 +15,15 @@ for app in fido:
|
||||
%>\
|
||||
|
||||
% 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
|
||||
/// 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.
|
||||
/// Returns default `ICON_WEBAUTHN` when the icon is not found or name not
|
||||
/// 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 {
|
||||
match icon_name.as_ref() {
|
||||
% for icon_name, var_name in icons:
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::theme;
|
||||
use crate::ui::{
|
||||
component::{label::Label, text::TextStyle, Child, Component, Event, EventCtx},
|
||||
display::{self, Color, Font},
|
||||
display::{self, toif::Icon, Color, Font},
|
||||
geometry::{Alignment, Insets, Offset, Rect},
|
||||
util::icon_text_center,
|
||||
};
|
||||
@ -100,7 +100,7 @@ where
|
||||
|
||||
pub struct NotificationFrame<T, U> {
|
||||
area: Rect,
|
||||
icon: &'static [u8],
|
||||
icon: Icon,
|
||||
title: U,
|
||||
content: Child<T>,
|
||||
}
|
||||
@ -116,7 +116,7 @@ where
|
||||
const ICON_SPACE: 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 {
|
||||
icon,
|
||||
title,
|
||||
@ -129,7 +129,7 @@ where
|
||||
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
|
||||
.inset(Insets::uniform(Self::BORDER))
|
||||
.split_top(Self::HEIGHT);
|
||||
|
@ -2,6 +2,7 @@ use crate::{
|
||||
time::Instant,
|
||||
ui::{
|
||||
component::{Child, Component, ComponentExt, Event, EventCtx, FixedHeightBar, Pad},
|
||||
display::toif::Icon,
|
||||
geometry::{Grid, Insets, Rect},
|
||||
util::animation_disabled,
|
||||
},
|
||||
@ -126,7 +127,7 @@ pub enum CancelHoldMsg {
|
||||
impl CancelHold {
|
||||
pub fn new(button_style: ButtonStyleSheet) -> FixedHeightBar<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")
|
||||
.styled(button_style)
|
||||
.into_child(),
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
trezorhal::usb::usb_configured,
|
||||
ui::{
|
||||
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},
|
||||
geometry::{Offset, Point, Rect},
|
||||
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 {
|
||||
2 => (theme::VIOLET, theme::ICON_MAGIC),
|
||||
1 => (theme::YELLOW, theme::ICON_WARN),
|
||||
_ => (theme::RED, theme::ICON_WARN),
|
||||
2 => (theme::VIOLET, Icon::new(theme::ICON_MAGIC)),
|
||||
1 => (theme::YELLOW, Icon::new(theme::ICON_WARN)),
|
||||
_ => (theme::RED, Icon::new(theme::ICON_WARN)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,7 +273,7 @@ where
|
||||
text: locked,
|
||||
style: theme::TEXT_BOLD,
|
||||
offset: Offset::new(10, LOCKED_Y),
|
||||
icon: Some(theme::ICON_LOCK),
|
||||
icon: Some(Icon::new(theme::ICON_LOCK)),
|
||||
},
|
||||
HomescreenText {
|
||||
text: tap,
|
||||
|
@ -7,12 +7,11 @@ use crate::{
|
||||
trezorhal::{
|
||||
buffers::{get_blurring_buffer, get_jpeg_buffer, get_jpeg_work_buffer, BufferJpeg},
|
||||
display,
|
||||
display::{bar_radius_buffer, ToifFormat},
|
||||
uzlib::UzlibContext,
|
||||
display::bar_radius_buffer,
|
||||
},
|
||||
ui::{
|
||||
constant::screen,
|
||||
display::{position_buffer, set_window, toif_info_ensure, Color},
|
||||
display::{position_buffer, set_window, Color},
|
||||
geometry::{Offset, Point, Rect},
|
||||
},
|
||||
};
|
||||
@ -20,7 +19,10 @@ use crate::{
|
||||
use crate::ui::{
|
||||
component::text::TextStyle,
|
||||
constant::{HEIGHT, WIDTH},
|
||||
display::tjpgd::{BufferInput, BufferOutput, JDEC},
|
||||
display::{
|
||||
tjpgd::{BufferInput, BufferOutput, JDEC},
|
||||
Icon,
|
||||
},
|
||||
model_tt::theme,
|
||||
util::icon_text_center,
|
||||
};
|
||||
@ -30,13 +32,13 @@ pub struct HomescreenText<'a> {
|
||||
pub text: &'a str,
|
||||
pub style: TextStyle,
|
||||
pub offset: Offset,
|
||||
pub icon: Option<&'static [u8]>,
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HomescreenNotification<'a> {
|
||||
pub text: &'a str,
|
||||
pub icon: &'static [u8],
|
||||
pub icon: Icon,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
@ -129,12 +131,10 @@ fn homescreen_position_text(
|
||||
let text_width_clamped = text_width.clamp(0, screen().width());
|
||||
|
||||
let icon_size = if let Some(icon) = text.icon {
|
||||
let (icon_size, icon_data) = toif_info_ensure(icon, ToifFormat::GrayScaleEH);
|
||||
assert!(icon_size.x <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
assert!(icon_size.y <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
let mut ctx = UzlibContext::new(icon_data, None);
|
||||
unwrap!(ctx.uncompress(icon_buffer), "Decompression failed");
|
||||
icon_size
|
||||
assert!(icon.toif.width() <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
assert!(icon.toif.height() <= HOMESCREEN_MAX_ICON_SIZE);
|
||||
icon.toif.uncompress(icon_buffer);
|
||||
icon.toif.size
|
||||
} else {
|
||||
Offset::zero()
|
||||
};
|
||||
|
@ -3,7 +3,8 @@ use crate::{
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
display,
|
||||
geometry::{Offset, Rect},
|
||||
display::toif::Icon,
|
||||
geometry::{Offset, Rect, CENTER},
|
||||
model_tt::{
|
||||
component::{
|
||||
keyboard::{
|
||||
@ -139,7 +140,7 @@ impl Component for Bip39Input {
|
||||
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
||||
// 16px from the right edge.
|
||||
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.set_stylesheet(ctx, theme::button_confirm());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM));
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||
} else {
|
||||
// Auto-complete button.
|
||||
self.button.enable(ctx);
|
||||
self.button.set_stylesheet(ctx, theme::button_default());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CLICK));
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CLICK)));
|
||||
}
|
||||
} else {
|
||||
// Disabled button.
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::ui::{
|
||||
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::{
|
||||
component::{Button, ButtonMsg},
|
||||
theme,
|
||||
@ -38,8 +39,8 @@ where
|
||||
back: Child::new(Maybe::hidden(
|
||||
theme::BG,
|
||||
Button::with_icon_blend(
|
||||
theme::IMAGE_BG_BACK_BTN_TALL,
|
||||
theme::ICON_BACK,
|
||||
Icon::new(theme::IMAGE_BG_BACK_BTN_TALL),
|
||||
Icon::new(theme::ICON_BACK),
|
||||
Offset::new(30, 17),
|
||||
)
|
||||
.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_size = self.prompt.inner().inner().max_size();
|
||||
let prompt_top_left = prompt_size.snap(prompt_center, Alignment::Center, Alignment::Center);
|
||||
let prompt_area = Rect::from_top_left_and_size(prompt_top_left, prompt_size);
|
||||
let prompt_area = Rect::snap(prompt_center, prompt_size, CENTER);
|
||||
|
||||
self.prompt.place(prompt_area);
|
||||
self.back.place(back_area);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::ui::{
|
||||
component::{base::ComponentExt, Child, Component, Event, EventCtx, Never},
|
||||
display,
|
||||
display::toif::Icon,
|
||||
geometry::{Grid, Insets, Offset, Rect},
|
||||
model_tt::component::{
|
||||
button::{Button, ButtonContent, ButtonMsg},
|
||||
@ -46,12 +47,12 @@ impl PassphraseKeyboard {
|
||||
Self {
|
||||
page_swipe: Swipe::horizontal(),
|
||||
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())
|
||||
.into_child(),
|
||||
back: Button::with_icon_blend(
|
||||
theme::IMAGE_BG_BACK_BTN,
|
||||
theme::ICON_BACK,
|
||||
Icon::new(theme::IMAGE_BG_BACK_BTN),
|
||||
Icon::new(theme::ICON_BACK),
|
||||
Offset::new(30, 12),
|
||||
)
|
||||
.styled(theme::button_reset())
|
||||
@ -61,7 +62,7 @@ impl PassphraseKeyboard {
|
||||
keys: KEYBOARD.map(|page| {
|
||||
page.map(|text| {
|
||||
if text == " " {
|
||||
let icon = theme::ICON_SPACE;
|
||||
let icon = Icon::new(theme::ICON_SPACE);
|
||||
Child::new(Button::with_icon(icon))
|
||||
} else {
|
||||
Child::new(Button::with_text(text))
|
||||
|
@ -9,9 +9,9 @@ use crate::{
|
||||
base::ComponentExt, text::TextStyle, Child, Component, Event, EventCtx, Label, Maybe,
|
||||
Never, Pad, TimerToken,
|
||||
},
|
||||
display::{self, Font},
|
||||
display::{self, toif::Icon, Font},
|
||||
event::TouchEvent,
|
||||
geometry::{Alignment, Grid, Insets, Offset, Rect},
|
||||
geometry::{Grid, Insets, Offset, Rect, CENTER, TOP_LEFT},
|
||||
model_tt::component::{
|
||||
button::{Button, ButtonContent, ButtonMsg, ButtonMsg::Clicked},
|
||||
theme,
|
||||
@ -70,8 +70,8 @@ where
|
||||
) -> Self {
|
||||
// Control buttons.
|
||||
let erase_btn = Button::with_icon_blend(
|
||||
theme::IMAGE_BG_BACK_BTN,
|
||||
theme::ICON_BACK,
|
||||
Icon::new(theme::IMAGE_BG_BACK_BTN),
|
||||
Icon::new(theme::ICON_BACK),
|
||||
Offset::new(30, 12),
|
||||
)
|
||||
.styled(theme::button_reset())
|
||||
@ -79,7 +79,8 @@ where
|
||||
.initially_enabled(false);
|
||||
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 =
|
||||
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),
|
||||
erase_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())
|
||||
.initially_enabled(false)
|
||||
.into_child(),
|
||||
@ -369,9 +370,7 @@ impl PinDots {
|
||||
}
|
||||
|
||||
fn paint_dots(&self, area: Rect) {
|
||||
let mut cursor = self
|
||||
.size()
|
||||
.snap(area.center(), Alignment::Center, Alignment::Center);
|
||||
let mut cursor = self.size().snap(area.center(), CENTER);
|
||||
|
||||
let digits = self.digits.len();
|
||||
let dots_visible = digits.min(MAX_VISIBLE_DOTS);
|
||||
@ -384,9 +383,9 @@ impl PinDots {
|
||||
|
||||
// Small leftmost dot.
|
||||
if digits > dots_visible + 1 {
|
||||
display::icon_top_left(
|
||||
Icon::new(theme::DOT_SMALL).draw(
|
||||
cursor - Offset::x(2 * step),
|
||||
theme::DOT_SMALL,
|
||||
TOP_LEFT,
|
||||
self.style.text_color,
|
||||
self.style.background_color,
|
||||
);
|
||||
@ -394,9 +393,9 @@ impl PinDots {
|
||||
|
||||
// Greyed out dot.
|
||||
if digits > dots_visible {
|
||||
display::icon_top_left(
|
||||
Icon::new(theme::DOT_ACTIVE).draw(
|
||||
cursor - Offset::x(step),
|
||||
theme::DOT_ACTIVE,
|
||||
TOP_LEFT,
|
||||
theme::GREY_LIGHT,
|
||||
self.style.background_color,
|
||||
);
|
||||
@ -404,9 +403,9 @@ impl PinDots {
|
||||
|
||||
// Draw a dot for each PIN digit.
|
||||
for _ in 0..dots_visible {
|
||||
display::icon_top_left(
|
||||
Icon::new(theme::DOT_ACTIVE).draw(
|
||||
cursor,
|
||||
theme::DOT_ACTIVE,
|
||||
TOP_LEFT,
|
||||
self.style.text_color,
|
||||
self.style.background_color,
|
||||
);
|
||||
|
@ -7,7 +7,8 @@ use crate::{
|
||||
ui::{
|
||||
component::{Component, Event, EventCtx},
|
||||
display,
|
||||
geometry::{Offset, Rect},
|
||||
display::toif::Icon,
|
||||
geometry::{Offset, Rect, CENTER},
|
||||
model_tt::{
|
||||
component::{
|
||||
keyboard::{
|
||||
@ -173,7 +174,7 @@ impl Component for Slip39Input {
|
||||
// Icon is painted in the right-center point, of expected size 16x16 pixels, and
|
||||
// 16px from the right edge.
|
||||
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.set_stylesheet(ctx, theme::button_confirm());
|
||||
self.button
|
||||
.set_content(ctx, ButtonContent::Icon(theme::ICON_CONFIRM));
|
||||
.set_content(ctx, ButtonContent::Icon(Icon::new(theme::ICON_CONFIRM)));
|
||||
} else {
|
||||
// Disabled button.
|
||||
self.button.disable(ctx);
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
ui::{
|
||||
animation::Animation,
|
||||
component::{Component, Event, EventCtx},
|
||||
display::{self, Color},
|
||||
display::{self, toif::Icon, Color},
|
||||
geometry::{Offset, Rect},
|
||||
model_tt::constant,
|
||||
util::animation_disabled,
|
||||
@ -173,6 +173,7 @@ impl Component for Loader {
|
||||
} else {
|
||||
self.styles.active
|
||||
};
|
||||
|
||||
display::loader(
|
||||
progress,
|
||||
self.offset_y,
|
||||
@ -190,7 +191,7 @@ pub struct LoaderStyleSheet {
|
||||
}
|
||||
|
||||
pub struct LoaderStyle {
|
||||
pub icon: Option<(&'static [u8], Color)>,
|
||||
pub icon: Option<(Icon, Color)>,
|
||||
pub loader_color: Color,
|
||||
pub background_color: Color,
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
mod button;
|
||||
mod dialog;
|
||||
mod fido;
|
||||
#[rustfmt::skip]
|
||||
mod fido_icons;
|
||||
mod frame;
|
||||
mod hold_to_confirm;
|
||||
|
@ -3,7 +3,7 @@ use crate::ui::{
|
||||
base::ComponentExt, paginated::PageMsg, Component, Event, EventCtx, FixedHeightBar, Label,
|
||||
Pad, Paginate,
|
||||
},
|
||||
display::{self, Color},
|
||||
display::{self, toif::Icon, Color},
|
||||
geometry::{Insets, Rect},
|
||||
model_tt::component::{Button, ButtonMsg},
|
||||
};
|
||||
@ -44,7 +44,7 @@ where
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
display,
|
||||
geometry::{LinearPlacement, Offset, Rect},
|
||||
display::toif::Icon,
|
||||
geometry::{LinearPlacement, Offset, Rect, CENTER},
|
||||
};
|
||||
|
||||
use super::theme;
|
||||
@ -76,11 +76,11 @@ impl Component for ScrollBar {
|
||||
}
|
||||
|
||||
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) {
|
||||
0 => theme::DOT_INACTIVE,
|
||||
1 => theme::DOT_INACTIVE_HALF,
|
||||
_ => theme::DOT_INACTIVE_QUARTER,
|
||||
0 => Icon::new(theme::DOT_INACTIVE),
|
||||
1 => Icon::new(theme::DOT_INACTIVE_HALF),
|
||||
_ => Icon::new(theme::DOT_INACTIVE_QUARTER),
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ impl Component for ScrollBar {
|
||||
);
|
||||
for i in first_shown..(last_shown + 1) {
|
||||
let icon = if i == self.active_page {
|
||||
theme::DOT_ACTIVE
|
||||
Icon::new(theme::DOT_ACTIVE)
|
||||
} else if i <= first_shown + 1 {
|
||||
let before_first_shown = 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;
|
||||
dotsize(last_shown - i, after_last_shown)
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use crate::{
|
||||
},
|
||||
Border, Component, Empty, Timeout, TimeoutMsg,
|
||||
},
|
||||
display::tjpgd::jpeg_info,
|
||||
display::{tjpgd::jpeg_info, toif::Icon},
|
||||
geometry,
|
||||
layout::{
|
||||
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(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||
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(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("NEXT").styled(theme::button_confirm()),
|
||||
2,
|
||||
);
|
||||
@ -701,7 +701,7 @@ fn new_show_modal(
|
||||
icon,
|
||||
title,
|
||||
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),
|
||||
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 {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let icon = BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_ERROR,
|
||||
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||
Icon::new(theme::IMAGE_FG_ERROR),
|
||||
theme::ERROR_COLOR,
|
||||
theme::FG,
|
||||
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(
|
||||
Button::with_icon(theme::ICON_CANCEL),
|
||||
Button::with_icon(Icon::new(theme::ICON_CANCEL)),
|
||||
Button::with_text("CONFIRM").styled(theme::button_confirm()),
|
||||
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 {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let icon = BlendedImage::new(
|
||||
theme::IMAGE_BG_TRIANGLE,
|
||||
theme::IMAGE_FG_WARN,
|
||||
Icon::new(theme::IMAGE_BG_TRIANGLE),
|
||||
Icon::new(theme::IMAGE_FG_WARN),
|
||||
theme::WARN_COLOR,
|
||||
theme::FG,
|
||||
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 {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let icon = BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_SUCCESS,
|
||||
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||
Icon::new(theme::IMAGE_FG_SUCCESS),
|
||||
theme::SUCCESS_COLOR,
|
||||
theme::FG,
|
||||
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 {
|
||||
let block = move |_args: &[Obj], kwargs: &Map| {
|
||||
let icon = BlendedImage::new(
|
||||
theme::IMAGE_BG_CIRCLE,
|
||||
theme::IMAGE_FG_INFO,
|
||||
Icon::new(theme::IMAGE_BG_CIRCLE),
|
||||
Icon::new(theme::IMAGE_FG_INFO),
|
||||
theme::INFO_COLOR,
|
||||
theme::FG,
|
||||
theme::BG,
|
||||
@ -1088,8 +1088,8 @@ extern "C" fn new_show_checklist(n_args: usize, args: *const Obj, kwargs: *mut M
|
||||
title,
|
||||
Dialog::new(
|
||||
Checklist::from_paragraphs(
|
||||
theme::ICON_LIST_CURRENT,
|
||||
theme::ICON_LIST_CHECK,
|
||||
Icon::new(theme::ICON_LIST_CURRENT),
|
||||
Icon::new(theme::ICON_LIST_CHECK),
|
||||
active,
|
||||
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 {
|
||||
LayoutObj::new(NotificationFrame::new(
|
||||
theme::ICON_WARN,
|
||||
Icon::new(theme::ICON_WARN),
|
||||
notification,
|
||||
Dialog::new(paragraphs, Button::<&'static str>::abort_info_enter()),
|
||||
))?
|
||||
} else {
|
||||
LayoutObj::new(NotificationFrame::new(
|
||||
theme::ICON_WARN,
|
||||
Icon::new(theme::ICON_WARN),
|
||||
notification,
|
||||
Dialog::new(paragraphs, Button::cancel_confirm_text(None, button)),
|
||||
))?
|
||||
|
@ -12,6 +12,7 @@ use crate::{
|
||||
|
||||
use super::component::{ButtonStyle, ButtonStyleSheet, LoaderStyle, LoaderStyleSheet};
|
||||
|
||||
use crate::ui::display::toif::NamedToif;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
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;
|
||||
|
||||
// UI icons (greyscale).
|
||||
pub const ICON_CANCEL: &[u8] = include_res!("model_tt/res/cancel.toif");
|
||||
pub const ICON_CONFIRM: &[u8] = include_res!("model_tt/res/confirm.toif");
|
||||
pub const ICON_SPACE: &[u8] = include_res!("model_tt/res/space.toif");
|
||||
pub const ICON_BACK: &[u8] = include_res!("model_tt/res/back.toif");
|
||||
pub const ICON_CLICK: &[u8] = include_res!("model_tt/res/click.toif");
|
||||
pub const ICON_NEXT: &[u8] = include_res!("model_tt/res/next.toif");
|
||||
pub const ICON_WARN: &[u8] = include_res!("model_tt/res/warn-icon.toif");
|
||||
pub const ICON_MAGIC: &[u8] = include_res!("model_tt/res/magic.toif");
|
||||
pub const ICON_LIST_CURRENT: &[u8] = include_res!("model_tt/res/current.toif");
|
||||
pub const ICON_LIST_CHECK: &[u8] = include_res!("model_tt/res/check.toif");
|
||||
pub const ICON_LOCK: &[u8] = include_res!("model_tt/res/lock.toif");
|
||||
pub const ICON_CANCEL: NamedToif = NamedToif(include_res!("model_tt/res/cancel.toif"), "cancel");
|
||||
pub const ICON_CONFIRM: NamedToif = NamedToif(include_res!("model_tt/res/confirm.toif"), "confirm");
|
||||
pub const ICON_SPACE: NamedToif = NamedToif(include_res!("model_tt/res/space.toif"), "space");
|
||||
pub const ICON_BACK: NamedToif = NamedToif(include_res!("model_tt/res/back.toif"), "back");
|
||||
pub const ICON_CLICK: NamedToif = NamedToif(include_res!("model_tt/res/click.toif"), "click");
|
||||
pub const ICON_NEXT: NamedToif = NamedToif(include_res!("model_tt/res/next.toif"), "next");
|
||||
pub const ICON_WARN: NamedToif = NamedToif(include_res!("model_tt/res/warn-icon.toif"), "warn");
|
||||
pub const ICON_MAGIC: NamedToif = NamedToif(include_res!("model_tt/res/magic.toif"), "magic");
|
||||
pub const ICON_LIST_CURRENT: NamedToif =
|
||||
NamedToif(include_res!("model_tt/res/current.toif"), "current");
|
||||
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.
|
||||
pub const WARN_COLOR: Color = YELLOW;
|
||||
pub const INFO_COLOR: Color = BLUE;
|
||||
pub const SUCCESS_COLOR: Color = GREEN;
|
||||
pub const ERROR_COLOR: Color = RED;
|
||||
pub const IMAGE_FG_WARN: &[u8] = include_res!("model_tt/res/warn_fg.toif");
|
||||
pub const IMAGE_FG_SUCCESS: &[u8] = include_res!("model_tt/res/success_fg.toif");
|
||||
pub const IMAGE_FG_ERROR: &[u8] = include_res!("model_tt/res/error_fg.toif");
|
||||
pub const IMAGE_FG_INFO: &[u8] = include_res!("model_tt/res/info_fg.toif");
|
||||
pub const IMAGE_BG_CIRCLE: &[u8] = include_res!("model_tt/res/circle.toif");
|
||||
pub const IMAGE_BG_TRIANGLE: &[u8] = include_res!("model_tt/res/triangle.toif");
|
||||
pub const IMAGE_BG_BACK_BTN: &[u8] = include_res!("model_tt/res/back_btn.toif");
|
||||
pub const IMAGE_BG_BACK_BTN_TALL: &[u8] = include_res!("model_tt/res/back_btn_tall.toif");
|
||||
pub const IMAGE_FG_WARN: NamedToif =
|
||||
NamedToif(include_res!("model_tt/res/warn_fg.toif"), "warn_fg");
|
||||
pub const IMAGE_FG_SUCCESS: NamedToif =
|
||||
NamedToif(include_res!("model_tt/res/success_fg.toif"), "success_fg");
|
||||
pub const IMAGE_FG_ERROR: NamedToif =
|
||||
NamedToif(include_res!("model_tt/res/error_fg.toif"), "error_fg");
|
||||
pub const IMAGE_FG_INFO: NamedToif =
|
||||
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
|
||||
pub const IMAGE_HOMESCREEN: &[u8] = include_res!("model_tt/res/bg.jpg");
|
||||
|
||||
// Scrollbar/PIN dots.
|
||||
pub const DOT_ACTIVE: &[u8] = include_res!("model_tt/res/scroll-active.toif");
|
||||
pub const DOT_INACTIVE: &[u8] = include_res!("model_tt/res/scroll-inactive.toif");
|
||||
pub const DOT_INACTIVE_HALF: &[u8] = include_res!("model_tt/res/scroll-inactive-half.toif");
|
||||
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_ACTIVE: NamedToif = NamedToif(
|
||||
include_res!("model_tt/res/scroll-active.toif"),
|
||||
"scroll-active",
|
||||
);
|
||||
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 {
|
||||
TEXT_NORMAL
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::ui::{
|
||||
component::text::TextStyle,
|
||||
display,
|
||||
geometry::{Offset, Point},
|
||||
display::toif::Icon,
|
||||
geometry::{Offset, Point, CENTER},
|
||||
};
|
||||
|
||||
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`.
|
||||
pub fn icon_text_center(
|
||||
baseline: Point,
|
||||
icon: &'static [u8],
|
||||
icon: Icon,
|
||||
space: i16,
|
||||
text: &str,
|
||||
style: TextStyle,
|
||||
text_offset: Offset,
|
||||
) {
|
||||
let toif_info = unwrap!(display::toif_info(icon), "Invalid TOIF data");
|
||||
let icon_width = toif_info.0.y;
|
||||
let icon_width = icon.toif.width();
|
||||
let text_width = style.text_font.text_width(text);
|
||||
let text_height = style.text_font.text_height();
|
||||
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.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)]
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user