mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-26 09:52:34 +00:00
feat(eckhart): allow multiline text in buttons
[no changelog]
This commit is contained in:
parent
f7e1444bba
commit
3f3fd4a021
@ -74,7 +74,9 @@ impl Component for BldMenu {
|
|||||||
let padding = 28;
|
let padding = 28;
|
||||||
|
|
||||||
for button in self.buttons.iter_mut() {
|
for button in self.buttons.iter_mut() {
|
||||||
let button_height = button.content_height() + 2 * padding;
|
let button_height = button
|
||||||
|
.content_height(button_width - 2 * Button::MENU_ITEM_CONTENT_OFFSET.x)
|
||||||
|
+ 2 * padding;
|
||||||
let button_bounds =
|
let button_bounds =
|
||||||
Rect::from_top_left_and_size(top_left, Offset::new(button_width, button_height));
|
Rect::from_top_left_and_size(top_left, Offset::new(button_width, button_height));
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
ui::{
|
ui::{
|
||||||
component::{text::TextStyle, Component, Event, EventCtx, Timer},
|
component::{text::TextStyle, Component, Event, EventCtx, Timer},
|
||||||
|
constant,
|
||||||
display::{toif::Icon, Color, Font},
|
display::{toif::Icon, Color, Font},
|
||||||
event::TouchEvent,
|
event::TouchEvent,
|
||||||
geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect},
|
geometry::{Alignment, Alignment2D, Insets, Offset, Point, Rect},
|
||||||
@ -58,7 +59,7 @@ impl Button {
|
|||||||
);
|
);
|
||||||
const MENU_ITEM_RADIUS: u8 = 12;
|
const MENU_ITEM_RADIUS: u8 = 12;
|
||||||
const MENU_ITEM_ALIGNMENT: Alignment = Alignment::Start;
|
const MENU_ITEM_ALIGNMENT: Alignment = Alignment::Start;
|
||||||
const MENU_ITEM_CONTENT_OFFSET: Offset = Offset::x(12);
|
pub const MENU_ITEM_CONTENT_OFFSET: Offset = Offset::x(12);
|
||||||
|
|
||||||
pub const fn new(content: ButtonContent) -> Self {
|
pub const fn new(content: ButtonContent) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -245,18 +246,36 @@ impl Button {
|
|||||||
self.style().font.visible_text_height("1")
|
self.style().font.visible_text_height("1")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_height(&self) -> i16 {
|
fn baseline_subtext_height(&self) -> i16 {
|
||||||
|
match &self.content {
|
||||||
|
ButtonContent::TextAndSubtext { subtext_style, .. } => {
|
||||||
|
subtext_style.text_font.visible_text_height("1")
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_height(&self, text: &str, width: i16) -> i16 {
|
||||||
|
let (t1, t2) = split_two_lines(text, self.stylesheet.normal.font, width);
|
||||||
|
if t1.is_empty() || t2.is_empty() {
|
||||||
|
self.style().font.line_height()
|
||||||
|
} else {
|
||||||
|
self.style().font.line_height() * 2 - constant::LINE_SPACE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn content_height(&self, width: i16) -> i16 {
|
||||||
match &self.content {
|
match &self.content {
|
||||||
ButtonContent::Empty => 0,
|
ButtonContent::Empty => 0,
|
||||||
ButtonContent::Text(_) => self.baseline_text_height(),
|
ButtonContent::Text(text) => text.map(|t| self.text_height(t, width)),
|
||||||
ButtonContent::Icon(icon) => icon.toif.height(),
|
ButtonContent::Icon(icon) => icon.toif.height(),
|
||||||
ButtonContent::IconAndText(child) => {
|
ButtonContent::IconAndText(child) => {
|
||||||
let text_height = self.baseline_text_height();
|
let text_height = self.style().font.line_height();
|
||||||
let icon_height = child.icon.toif.height();
|
let icon_height = child.icon.toif.height();
|
||||||
text_height.max(icon_height)
|
text_height.max(icon_height)
|
||||||
}
|
}
|
||||||
ButtonContent::TextAndSubtext { subtext_style, .. } => {
|
ButtonContent::TextAndSubtext { text, .. } => {
|
||||||
self.style().font.line_height() + subtext_style.text_font.text_height()
|
text.map(|t| self.text_height(t, width) + self.baseline_subtext_height())
|
||||||
}
|
}
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
ButtonContent::HomeBar(_) => theme::ACTION_BAR_HEIGHT,
|
ButtonContent::HomeBar(_) => theme::ACTION_BAR_HEIGHT,
|
||||||
@ -371,21 +390,45 @@ impl Button {
|
|||||||
stylesheet: &ButtonStyle,
|
stylesheet: &ButtonStyle,
|
||||||
alpha: u8,
|
alpha: u8,
|
||||||
) {
|
) {
|
||||||
|
let mut show_text = |text: &str, render_origin: Point| {
|
||||||
|
shape::Text::new(render_origin, text, stylesheet.font)
|
||||||
|
.with_fg(stylesheet.text_color)
|
||||||
|
.with_align(self.text_align)
|
||||||
|
.with_alpha(alpha)
|
||||||
|
.render(target)
|
||||||
|
};
|
||||||
|
let render_origin = |y_offset: i16| {
|
||||||
|
match self.text_align {
|
||||||
|
Alignment::Start => self.area.left_center().ofs(self.content_offset),
|
||||||
|
Alignment::Center => self.area.center().ofs(self.content_offset),
|
||||||
|
Alignment::End => self.area.right_center().ofs(self.content_offset.neg()),
|
||||||
|
}
|
||||||
|
.ofs(Offset::y(y_offset))
|
||||||
|
};
|
||||||
|
|
||||||
match &self.content {
|
match &self.content {
|
||||||
ButtonContent::Empty => {}
|
ButtonContent::Empty => {}
|
||||||
ButtonContent::Text(text) => {
|
ButtonContent::Text(text) => {
|
||||||
let render_origin = match self.text_align {
|
let text_baseline_height = self.baseline_text_height();
|
||||||
Alignment::Start => self.area.left_center().ofs(self.content_offset),
|
text.map(|t| {
|
||||||
Alignment::Center => self.area.center().ofs(self.content_offset),
|
let (t1, t2) = split_two_lines(
|
||||||
Alignment::End => self.area.right_center().ofs(self.content_offset.neg()),
|
t,
|
||||||
}
|
stylesheet.font,
|
||||||
.ofs(Offset::y(self.content_height() / 2));
|
self.area.width() - 2 * self.content_offset.x,
|
||||||
text.map(|text| {
|
);
|
||||||
shape::Text::new(render_origin, text, stylesheet.font)
|
|
||||||
.with_fg(stylesheet.text_color)
|
if t1.is_empty() || t2.is_empty() {
|
||||||
.with_align(self.text_align)
|
show_text(t, render_origin(text_baseline_height / 2));
|
||||||
.with_alpha(alpha)
|
} else {
|
||||||
.render(target);
|
show_text(
|
||||||
|
t1,
|
||||||
|
render_origin(-(text_baseline_height / 2 + constant::LINE_SPACE)),
|
||||||
|
);
|
||||||
|
show_text(
|
||||||
|
t2,
|
||||||
|
render_origin(text_baseline_height + constant::LINE_SPACE * 2),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ButtonContent::TextAndSubtext {
|
ButtonContent::TextAndSubtext {
|
||||||
@ -393,30 +436,50 @@ impl Button {
|
|||||||
subtext,
|
subtext,
|
||||||
subtext_style,
|
subtext_style,
|
||||||
} => {
|
} => {
|
||||||
let base = match self.text_align {
|
let text_baseline_height = self.baseline_text_height();
|
||||||
Alignment::Start => self.area.left_center().ofs(self.content_offset),
|
let single_line_text = text.map(|t| {
|
||||||
Alignment::Center => self.area.center().ofs(self.content_offset),
|
let (t1, t2) = split_two_lines(
|
||||||
Alignment::End => self.area.right_center().ofs(self.content_offset.neg()),
|
t,
|
||||||
};
|
stylesheet.font,
|
||||||
|
self.area.width() - 2 * self.content_offset.x,
|
||||||
let text_render_origin = base
|
);
|
||||||
.ofs(Offset::y(self.content_height() / 2 - self.baseline_text_height()).neg());
|
if t1.is_empty() || t2.is_empty() {
|
||||||
let subtext_render_origin = base.ofs(Offset::y(self.content_height() / 2));
|
show_text(
|
||||||
|
t,
|
||||||
text.map(|t| {
|
render_origin(text_baseline_height / 2 - constant::LINE_SPACE * 2),
|
||||||
shape::Text::new(text_render_origin, t, stylesheet.font)
|
);
|
||||||
.with_fg(stylesheet.text_color)
|
true
|
||||||
.with_align(self.text_align)
|
} else {
|
||||||
.with_alpha(alpha)
|
show_text(
|
||||||
.render(target);
|
t1,
|
||||||
|
render_origin(-(text_baseline_height / 2 + constant::LINE_SPACE * 3)),
|
||||||
|
);
|
||||||
|
show_text(
|
||||||
|
t2,
|
||||||
|
render_origin(text_baseline_height - constant::LINE_SPACE * 2),
|
||||||
|
);
|
||||||
|
false
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
subtext.map(|subtext| {
|
subtext.map(|subtext| {
|
||||||
shape::Text::new(subtext_render_origin, subtext, subtext_style.text_font)
|
shape::Text::new(
|
||||||
.with_fg(subtext_style.text_color)
|
render_origin(if single_line_text {
|
||||||
.with_align(self.text_align)
|
text_baseline_height / 2
|
||||||
.with_alpha(alpha)
|
+ constant::LINE_SPACE
|
||||||
.render(target);
|
+ self.baseline_subtext_height()
|
||||||
|
} else {
|
||||||
|
text_baseline_height
|
||||||
|
+ constant::LINE_SPACE * 2
|
||||||
|
+ self.baseline_subtext_height()
|
||||||
|
}),
|
||||||
|
subtext,
|
||||||
|
subtext_style.text_font,
|
||||||
|
)
|
||||||
|
.with_fg(subtext_style.text_color)
|
||||||
|
.with_align(self.text_align)
|
||||||
|
.with_alpha(alpha)
|
||||||
|
.render(target);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ButtonContent::Icon(icon) => {
|
ButtonContent::Icon(icon) => {
|
||||||
|
@ -300,7 +300,9 @@ impl<T: MenuItems> Component for VerticalMenu<T> {
|
|||||||
|
|
||||||
// Place each button (might overflow the menu bounds)
|
// Place each button (might overflow the menu bounds)
|
||||||
for button in self.buttons.iter_mut() {
|
for button in self.buttons.iter_mut() {
|
||||||
let button_height = button.content_height() + 2 * Self::MENU_ITEM_CONTENT_PADDING;
|
let button_height = button
|
||||||
|
.content_height(button_width - 2 * Button::MENU_ITEM_CONTENT_OFFSET.x)
|
||||||
|
+ 2 * Self::MENU_ITEM_CONTENT_PADDING;
|
||||||
let button_bounds =
|
let button_bounds =
|
||||||
Rect::from_top_left_and_size(top_left, Offset::new(button_width, button_height));
|
Rect::from_top_left_and_size(top_left, Offset::new(button_width, button_height));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user