mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-26 01:42:34 +00:00
feat(eckhart): improve Homescreen styling
- correct Homebar icons - colors and gradients - LED usage - Hint changes: add pad and cleanup some unused code [no changelog]
This commit is contained in:
parent
2196a5f785
commit
af02e5e6a2
@ -528,7 +528,7 @@ impl Button {
|
|||||||
ButtonContent::HomeBar(text) => {
|
ButtonContent::HomeBar(text) => {
|
||||||
let baseline = self.area.center();
|
let baseline = self.area.center();
|
||||||
if let Some(text) = text {
|
if let Some(text) = text {
|
||||||
const OFFSET_Y: Offset = Offset::y(25);
|
const OFFSET_Y: Offset = Offset::y(16);
|
||||||
text.map(|text| {
|
text.map(|text| {
|
||||||
shape::Text::new(baseline, text, stylesheet.font)
|
shape::Text::new(baseline, text, stylesheet.font)
|
||||||
.with_fg(stylesheet.text_color)
|
.with_fg(stylesheet.text_color)
|
||||||
@ -536,23 +536,14 @@ impl Button {
|
|||||||
.with_alpha(alpha)
|
.with_alpha(alpha)
|
||||||
.render(target);
|
.render(target);
|
||||||
});
|
});
|
||||||
shape::ToifImage::new(
|
shape::ToifImage::new(self.area.center() + OFFSET_Y, theme::ICON_MINUS.toif)
|
||||||
self.area.center() + OFFSET_Y,
|
.with_fg(stylesheet.icon_color)
|
||||||
theme::ICON_DASH_HORIZONTAL.toif,
|
|
||||||
)
|
|
||||||
.with_fg(stylesheet.icon_color)
|
|
||||||
.with_align(Alignment2D::CENTER)
|
|
||||||
.render(target);
|
|
||||||
} else {
|
|
||||||
// double dash icon in the middle
|
|
||||||
const OFFSET_Y: Offset = Offset::y(5);
|
|
||||||
shape::ToifImage::new(baseline - OFFSET_Y, theme::ICON_DASH_HORIZONTAL.toif)
|
|
||||||
.with_fg(theme::GREY_LIGHT)
|
|
||||||
.with_align(Alignment2D::CENTER)
|
.with_align(Alignment2D::CENTER)
|
||||||
.render(target);
|
.render(target);
|
||||||
|
} else {
|
||||||
shape::ToifImage::new(baseline + OFFSET_Y, theme::ICON_DASH_HORIZONTAL.toif)
|
// Menu icon in the middle
|
||||||
.with_fg(theme::GREY_LIGHT)
|
shape::ToifImage::new(baseline, theme::ICON_MENU.toif)
|
||||||
|
.with_fg(stylesheet.icon_color)
|
||||||
.with_align(Alignment2D::CENTER)
|
.with_align(Alignment2D::CENTER)
|
||||||
.render(target);
|
.render(target);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::TString,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::TextStyle, Component, Event, EventCtx, Label, Never},
|
component::{text::TextStyle, Component, Event, EventCtx, Label, Never, Pad},
|
||||||
constant::screen,
|
constant::screen,
|
||||||
display::{Color, Icon},
|
display::{Color, Icon},
|
||||||
geometry::{Alignment, Alignment2D, Direction, Insets, Offset, Point, Rect},
|
geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect},
|
||||||
shape::{self, Renderer, Text},
|
shape::{self, Renderer, Text},
|
||||||
util::Pager,
|
util::Pager,
|
||||||
},
|
},
|
||||||
@ -20,12 +20,9 @@ use super::{super::fonts, theme};
|
|||||||
/// The instruction has adaptive height, depending on the text length. The
|
/// The instruction has adaptive height, depending on the text length. The
|
||||||
/// PageCounter is always of minimal component height (40px).
|
/// PageCounter is always of minimal component height (40px).
|
||||||
pub struct Hint<'a> {
|
pub struct Hint<'a> {
|
||||||
area: Rect,
|
content_area: Rect,
|
||||||
content: HintContent<'a>,
|
content: HintContent<'a>,
|
||||||
swipe_allow_up: bool,
|
pad: Pad,
|
||||||
swipe_allow_down: bool,
|
|
||||||
progress: i16,
|
|
||||||
dir: Direction,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
@ -44,12 +41,9 @@ impl<'a> Hint<'a> {
|
|||||||
|
|
||||||
fn from_content(content: HintContent<'a>) -> Self {
|
fn from_content(content: HintContent<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
area: Rect::zero(),
|
content_area: Rect::zero(),
|
||||||
content,
|
content,
|
||||||
swipe_allow_down: false,
|
pad: Pad::with_background(theme::BG),
|
||||||
swipe_allow_up: false,
|
|
||||||
progress: 0,
|
|
||||||
dir: Direction::Up,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,11 +63,21 @@ impl<'a> Hint<'a> {
|
|||||||
Self::from_content(HintContent::Instruction(instruction_component))
|
Self::from_content(HintContent::Instruction(instruction_component))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_warning<T: Into<TString<'static>>>(text: T) -> Self {
|
pub fn new_warning_neutral<T: Into<TString<'static>>>(text: T) -> Self {
|
||||||
|
let instruction_component = Instruction::new(
|
||||||
|
text.into(),
|
||||||
|
theme::GREY_LIGHT,
|
||||||
|
Some(theme::ICON_WARNING),
|
||||||
|
Some(theme::YELLOW),
|
||||||
|
);
|
||||||
|
Self::from_content(HintContent::Instruction(instruction_component))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_warning_caution<T: Into<TString<'static>>>(text: T) -> Self {
|
||||||
let instruction_component = Instruction::new(
|
let instruction_component = Instruction::new(
|
||||||
text.into(),
|
text.into(),
|
||||||
theme::GREY,
|
theme::GREY,
|
||||||
Some(theme::ICON_INFO),
|
Some(theme::ICON_WARNING),
|
||||||
Some(theme::ORANGE),
|
Some(theme::ORANGE),
|
||||||
);
|
);
|
||||||
Self::from_content(HintContent::Instruction(instruction_component))
|
Self::from_content(HintContent::Instruction(instruction_component))
|
||||||
@ -93,25 +97,9 @@ impl<'a> Hint<'a> {
|
|||||||
Self::from_content(HintContent::PageCounter(PageCounter::new()))
|
Self::from_content(HintContent::PageCounter(PageCounter::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_swipe(self, swipe_direction: Direction) -> Self {
|
|
||||||
match swipe_direction {
|
|
||||||
Direction::Up => Self {
|
|
||||||
swipe_allow_up: true,
|
|
||||||
..self
|
|
||||||
},
|
|
||||||
Direction::Down => Self {
|
|
||||||
swipe_allow_down: true,
|
|
||||||
..self
|
|
||||||
},
|
|
||||||
_ => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, pager: Pager) {
|
pub fn update(&mut self, pager: Pager) {
|
||||||
if let HintContent::PageCounter(counter) = &mut self.content {
|
if let HintContent::PageCounter(counter) = &mut self.content {
|
||||||
counter.update(pager);
|
counter.update(pager);
|
||||||
self.swipe_allow_down = counter.pager.is_first();
|
|
||||||
self.swipe_allow_up = counter.pager.is_last();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +116,12 @@ impl<'a> Component for Hint<'a> {
|
|||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
debug_assert!(bounds.width() == screen().width());
|
debug_assert!(bounds.width() == screen().width());
|
||||||
debug_assert!(bounds.height() == self.height());
|
debug_assert!(bounds.height() == self.height());
|
||||||
|
|
||||||
|
let pad_area = bounds
|
||||||
|
.inset(Insets::top(Self::HINT_INSETS.top))
|
||||||
|
.inset(Insets::bottom(Self::HINT_INSETS.bottom));
|
||||||
|
self.pad.place(pad_area);
|
||||||
|
|
||||||
let bounds = bounds.inset(Self::HINT_INSETS);
|
let bounds = bounds.inset(Self::HINT_INSETS);
|
||||||
|
|
||||||
if let HintContent::Instruction(instruction) = &mut self.content {
|
if let HintContent::Instruction(instruction) = &mut self.content {
|
||||||
@ -137,8 +131,8 @@ impl<'a> Component for Hint<'a> {
|
|||||||
};
|
};
|
||||||
instruction.label.place(text_area);
|
instruction.label.place(text_area);
|
||||||
}
|
}
|
||||||
self.area = bounds;
|
self.content_area = bounds;
|
||||||
self.area
|
self.content_area
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||||
@ -146,7 +140,8 @@ impl<'a> Component for Hint<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
self.content.render(self.area, target);
|
self.pad.render(target);
|
||||||
|
self.content.render(self.content_area, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +178,6 @@ struct Instruction<'a> {
|
|||||||
impl<'a> Instruction<'a> {
|
impl<'a> Instruction<'a> {
|
||||||
/// default style for instruction text
|
/// default style for instruction text
|
||||||
const STYLE_INSTRUCTION: &'static TextStyle = &theme::firmware::TEXT_SMALL;
|
const STYLE_INSTRUCTION: &'static TextStyle = &theme::firmware::TEXT_SMALL;
|
||||||
/// margin between icon and text
|
|
||||||
const ICON_OFFSET: Offset = Offset::x(24); // [px]
|
|
||||||
|
|
||||||
fn new(
|
fn new(
|
||||||
text: TString<'a>,
|
text: TString<'a>,
|
||||||
@ -205,7 +198,7 @@ impl<'a> Instruction<'a> {
|
|||||||
/// Calculates the width needed for the icon
|
/// Calculates the width needed for the icon
|
||||||
fn icon_width(&self) -> i16 {
|
fn icon_width(&self) -> i16 {
|
||||||
self.icon
|
self.icon
|
||||||
.map_or(0, |icon| icon.toif.width() + Self::ICON_OFFSET.x)
|
.map_or(0, |icon| icon.toif.width() + theme::PADDING)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the height needed for the Instruction text to be rendered.
|
/// Calculates the height needed for the Instruction text to be rendered.
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#[cfg(feature = "rgb_led")]
|
||||||
|
use crate::trezorhal::rgb_led;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
io::BinaryData,
|
io::BinaryData,
|
||||||
@ -69,33 +72,23 @@ impl Homescreen {
|
|||||||
let shadow = image.is_some();
|
let shadow = image.is_some();
|
||||||
|
|
||||||
// Notification
|
// Notification
|
||||||
// TODO: better notification handling
|
|
||||||
let mut notification_level = 4;
|
let mut notification_level = 4;
|
||||||
let mut hint = None;
|
let (led_color, hint) = match notification {
|
||||||
let mut led_color;
|
Some((text, level)) => {
|
||||||
if let Some((text, level)) = notification {
|
notification_level = level;
|
||||||
notification_level = level;
|
let (led_color, hint) = Self::get_notification_display(level, text);
|
||||||
if notification_level == 0 {
|
(Some(led_color), Some(hint))
|
||||||
led_color = Some(theme::RED);
|
|
||||||
hint = Some(Hint::new_warning_danger(text));
|
|
||||||
} else {
|
|
||||||
led_color = Some(theme::YELLOW);
|
|
||||||
hint = Some(Hint::new_instruction(text, Some(theme::ICON_INFO)));
|
|
||||||
}
|
}
|
||||||
} else if locked && coinjoin_authorized {
|
None if locked && coinjoin_authorized => (
|
||||||
led_color = Some(theme::GREEN_LIME);
|
Some(theme::GREEN_LIME),
|
||||||
hint = Some(Hint::new_instruction_green(
|
Some(Hint::new_instruction_green(
|
||||||
TR::coinjoin__do_not_disconnect,
|
TR::coinjoin__do_not_disconnect,
|
||||||
Some(theme::ICON_INFO),
|
Some(theme::ICON_INFO),
|
||||||
));
|
)),
|
||||||
} else {
|
),
|
||||||
led_color = Some(theme::GREY_LIGHT);
|
None => (None, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
if locked {
|
|
||||||
led_color = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
label: HomeLabel::new(label, shadow),
|
label: HomeLabel::new(label, shadow),
|
||||||
hint,
|
hint,
|
||||||
@ -126,6 +119,22 @@ impl Homescreen {
|
|||||||
ButtonContent::HomeBar(text)
|
ButtonContent::HomeBar(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_notification_display(level: u8, text: TString<'static>) -> (Color, Hint<'static>) {
|
||||||
|
match level {
|
||||||
|
0 => (theme::RED, Hint::new_warning_danger(text)),
|
||||||
|
1 => (theme::YELLOW, Hint::new_warning_neutral(text)),
|
||||||
|
2 => (theme::BLUE, Hint::new_instruction(text, None)),
|
||||||
|
3 => (
|
||||||
|
theme::GREEN_LIGHT,
|
||||||
|
Hint::new_instruction_green(text, Some(theme::ICON_INFO)),
|
||||||
|
),
|
||||||
|
_ => (
|
||||||
|
theme::GREY_LIGHT,
|
||||||
|
Hint::new_instruction(text, Some(theme::ICON_INFO)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn event_fuel_gauge(&mut self, ctx: &mut EventCtx, event: Event) {
|
fn event_fuel_gauge(&mut self, ctx: &mut EventCtx, event: Event) {
|
||||||
if animation_disabled() {
|
if animation_disabled() {
|
||||||
return;
|
return;
|
||||||
@ -151,6 +160,14 @@ impl Homescreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Homescreen {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Turn off the LED when homescreen is destroyed
|
||||||
|
#[cfg(feature = "rgb_led")]
|
||||||
|
rgb_led::set_color(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Homescreen {
|
impl Component for Homescreen {
|
||||||
type Msg = HomescreenMsg;
|
type Msg = HomescreenMsg;
|
||||||
|
|
||||||
@ -210,6 +227,13 @@ impl Component for Homescreen {
|
|||||||
if self.fuel_gauge.should_be_shown() {
|
if self.fuel_gauge.should_be_shown() {
|
||||||
self.fuel_gauge.render(target);
|
self.fuel_gauge.render(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rgb_led")]
|
||||||
|
if let Some(rgb_led) = self.led_color {
|
||||||
|
rgb_led::set_color(rgb_led.to_u32());
|
||||||
|
} else {
|
||||||
|
rgb_led::set_color(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,12 +373,12 @@ pub const fn menu_item_title_red() -> ButtonStyleSheet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! button_homebar_style {
|
macro_rules! button_homebar_style {
|
||||||
($text_color:expr, $icon_color:expr) => {
|
($button_color:expr, $icon_color:expr) => {
|
||||||
ButtonStyleSheet {
|
ButtonStyleSheet {
|
||||||
normal: &ButtonStyle {
|
normal: &ButtonStyle {
|
||||||
font: fonts::FONT_SATOSHI_MEDIUM_26,
|
font: fonts::FONT_SATOSHI_MEDIUM_26,
|
||||||
text_color: $text_color,
|
text_color: GREY_LIGHT,
|
||||||
button_color: BG,
|
button_color: $button_color,
|
||||||
icon_color: $icon_color,
|
icon_color: $icon_color,
|
||||||
background_color: BG,
|
background_color: BG,
|
||||||
},
|
},
|
||||||
@ -389,6 +389,7 @@ macro_rules! button_homebar_style {
|
|||||||
icon_color: GREY_LIGHT,
|
icon_color: GREY_LIGHT,
|
||||||
background_color: GREY_SUPER_DARK,
|
background_color: GREY_SUPER_DARK,
|
||||||
},
|
},
|
||||||
|
// unused
|
||||||
disabled: &ButtonStyle {
|
disabled: &ButtonStyle {
|
||||||
font: fonts::FONT_SATOSHI_MEDIUM_26,
|
font: fonts::FONT_SATOSHI_MEDIUM_26,
|
||||||
text_color: GREY_LIGHT,
|
text_color: GREY_LIGHT,
|
||||||
@ -399,14 +400,14 @@ macro_rules! button_homebar_style {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub const fn button_homebar_style(level: u8) -> ButtonStyleSheet {
|
pub const fn button_homebar_style(notification_level: u8) -> ButtonStyleSheet {
|
||||||
// NOTE: 0 is the highest severity.
|
// NOTE: 0 is the highest severity.
|
||||||
match level {
|
match notification_level {
|
||||||
4 => button_homebar_style!(GREY_LIGHT, GREY_LIGHT),
|
0 => button_homebar_style!(ORANGE_SUPER_DARK, RED),
|
||||||
3 => button_homebar_style!(GREY_LIGHT, GREEN_LIME),
|
1 => button_homebar_style!(YELLOW_DARK, GREY_LIGHT),
|
||||||
2 => button_homebar_style!(GREY_LIGHT, YELLOW),
|
2 => button_homebar_style!(GREY_SUPER_DARK, GREY_LIGHT),
|
||||||
1 => button_homebar_style!(GREY_LIGHT, YELLOW),
|
3 => button_homebar_style!(GREEN_DARK, GREY_LIGHT),
|
||||||
_ => button_homebar_style!(RED, RED),
|
_ => button_homebar_style!(GREY_EXTRA_DARK, GREY_LIGHT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +40,10 @@ pub const ORANGE: Color = Color::rgb(0xFF, 0x63, 0x30);
|
|||||||
pub const ORANGE_DIMMED: Color = Color::rgb(0x9E, 0x57, 0x42);
|
pub const ORANGE_DIMMED: Color = Color::rgb(0x9E, 0x57, 0x42);
|
||||||
pub const ORANGE_DARK: Color = Color::rgb(0x18, 0x0C, 0x0A);
|
pub const ORANGE_DARK: Color = Color::rgb(0x18, 0x0C, 0x0A);
|
||||||
pub const ORANGE_EXTRA_DARK: Color = Color::rgb(0x12, 0x07, 0x04);
|
pub const ORANGE_EXTRA_DARK: Color = Color::rgb(0x12, 0x07, 0x04);
|
||||||
|
pub const ORANGE_SUPER_DARK: Color = Color::rgb(0x2A, 0x0A, 0x00); // Homescreen gradient
|
||||||
|
|
||||||
pub const YELLOW: Color = Color::rgb(0xFF, 0xE4, 0x58);
|
pub const YELLOW: Color = Color::rgb(0xFF, 0xE4, 0x58);
|
||||||
|
pub const YELLOW_DARK: Color = Color::rgb(0x21, 0x1E, 0x0C); // Homescreen gradient
|
||||||
|
|
||||||
pub const BLUE: Color = Color::rgb(0x00, 0x46, 0xFF);
|
pub const BLUE: Color = Color::rgb(0x00, 0x46, 0xFF);
|
||||||
|
|
||||||
@ -50,11 +52,11 @@ pub const FATAL_ERROR_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
|||||||
pub const FATAL_ERROR_HIGHLIGHT_COLOR: Color = Color::rgb(0xFF, 0x41, 0x41);
|
pub const FATAL_ERROR_HIGHLIGHT_COLOR: Color = Color::rgb(0xFF, 0x41, 0x41);
|
||||||
|
|
||||||
// Common constants
|
// Common constants
|
||||||
pub const PADDING: i16 = 24; // px
|
pub const PADDING: i16 = 24; // [px]
|
||||||
pub const HEADER_HEIGHT: i16 = 96; // [px]
|
pub const HEADER_HEIGHT: i16 = 96; // [px]
|
||||||
pub const SIDE_INSETS: Insets = Insets::sides(PADDING);
|
pub const SIDE_INSETS: Insets = Insets::sides(PADDING);
|
||||||
pub const ACTION_BAR_HEIGHT: i16 = 90; // [px]
|
pub const ACTION_BAR_HEIGHT: i16 = 90; // [px]
|
||||||
pub const PARAGRAPHS_SPACING: i16 = 12; // px
|
pub const PARAGRAPHS_SPACING: i16 = 12; // [px]
|
||||||
pub const TEXT_VERTICAL_SPACING: i16 = 24; // [px]
|
pub const TEXT_VERTICAL_SPACING: i16 = 24; // [px]
|
||||||
|
|
||||||
// Tile pattern grid constants
|
// Tile pattern grid constants
|
||||||
|
@ -454,7 +454,7 @@ impl FirmwareUI for UIEckhart {
|
|||||||
screen = screen.with_hint(Hint::new_page_counter());
|
screen = screen.with_hint(Hint::new_page_counter());
|
||||||
}
|
}
|
||||||
if let Some(warning_footer) = warning_footer {
|
if let Some(warning_footer) = warning_footer {
|
||||||
screen = screen.with_hint(Hint::new_warning(warning_footer));
|
screen = screen.with_hint(Hint::new_warning_caution(warning_footer));
|
||||||
}
|
}
|
||||||
LayoutObj::new(screen)
|
LayoutObj::new(screen)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user