mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +00:00
feat(core/ui): page break icons
Co-authored-by: grdddj <jiri.musil06@seznam.cz> [no changelog]
This commit is contained in:
parent
60aa2e7292
commit
1b94a7cb7b
BIN
core/assets/page-next.png
Normal file
BIN
core/assets/page-next.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 146 B |
BIN
core/assets/page-prev.png
Normal file
BIN
core/assets/page-prev.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 296 B |
@ -160,6 +160,7 @@ fn select_line_breaks(
|
|||||||
|
|
||||||
pub trait GlyphMetrics {
|
pub trait GlyphMetrics {
|
||||||
fn char_width(&self, ch: char) -> i16;
|
fn char_width(&self, ch: char) -> i16;
|
||||||
|
fn text_width(&self, text: &str) -> i16;
|
||||||
fn line_height(&self) -> i16;
|
fn line_height(&self) -> i16;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,6 +169,10 @@ impl GlyphMetrics for Font {
|
|||||||
Font::char_width(*self, ch)
|
Font::char_width(*self, ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_width(&self, text: &str) -> i16 {
|
||||||
|
Font::text_width(*self, text)
|
||||||
|
}
|
||||||
|
|
||||||
fn line_height(&self) -> i16 {
|
fn line_height(&self) -> i16 {
|
||||||
Font::line_height(*self)
|
Font::line_height(*self)
|
||||||
}
|
}
|
||||||
@ -217,6 +222,10 @@ mod tests {
|
|||||||
fn line_height(&self) -> i16 {
|
fn line_height(&self) -> i16 {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_width(&self, text: &str) -> i16 {
|
||||||
|
self.width * text.len() as i16
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn break_text(s: &str, w: i16) -> Vec<Span> {
|
fn break_text(s: &str, w: i16) -> Vec<Span> {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use super::iter::GlyphMetrics;
|
use super::iter::GlyphMetrics;
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
display,
|
display,
|
||||||
display::{Color, Font},
|
display::{toif::Icon, Color, Font},
|
||||||
geometry::{Alignment, Dimensions, Offset, Point, Rect},
|
geometry::{Alignment, Dimensions, Offset, Point, Rect, BOTTOM_LEFT},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ELLIPSIS: &str = "...";
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum LineBreaking {
|
pub enum LineBreaking {
|
||||||
/// Break line only at whitespace, if possible. If we don't find any
|
/// Break line only at whitespace, if possible. If we don't find any
|
||||||
@ -24,6 +26,9 @@ pub enum PageBreaking {
|
|||||||
/// Before stopping at the bottom-right edge, insert ellipsis to signify
|
/// Before stopping at the bottom-right edge, insert ellipsis to signify
|
||||||
/// more content is available, but only if no hyphen has been inserted yet.
|
/// more content is available, but only if no hyphen has been inserted yet.
|
||||||
CutAndInsertEllipsis,
|
CutAndInsertEllipsis,
|
||||||
|
/// Same as `CutAndInsertEllipsis` but also insert an ellipsis/icon at the
|
||||||
|
/// beginning of the next page.
|
||||||
|
CutAndInsertEllipsisBoth,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visual instructions for laying out a formatted block of text.
|
/// Visual instructions for laying out a formatted block of text.
|
||||||
@ -43,6 +48,9 @@ pub struct TextLayout {
|
|||||||
pub style: TextStyle,
|
pub style: TextStyle,
|
||||||
/// Horizontal alignment.
|
/// Horizontal alignment.
|
||||||
pub align: Alignment,
|
pub align: Alignment,
|
||||||
|
|
||||||
|
/// Whether to draw "..." (or an icon) at the beginning.
|
||||||
|
pub continues_from_prev_page: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -58,6 +66,10 @@ pub struct TextStyle {
|
|||||||
pub hyphen_color: Color,
|
pub hyphen_color: Color,
|
||||||
/// Foreground color used for drawing the ellipsis.
|
/// Foreground color used for drawing the ellipsis.
|
||||||
pub ellipsis_color: Color,
|
pub ellipsis_color: Color,
|
||||||
|
/// Optional icon shown as ellipsis.
|
||||||
|
pub ellipsis_icon: Option<Icon>,
|
||||||
|
/// Optional icon to signal content continues from previous page.
|
||||||
|
pub prev_page_ellipsis_icon: Option<Icon>,
|
||||||
|
|
||||||
/// Specifies which line-breaking strategy to use.
|
/// Specifies which line-breaking strategy to use.
|
||||||
pub line_breaking: LineBreaking,
|
pub line_breaking: LineBreaking,
|
||||||
@ -79,6 +91,8 @@ impl TextStyle {
|
|||||||
background_color,
|
background_color,
|
||||||
hyphen_color,
|
hyphen_color,
|
||||||
ellipsis_color,
|
ellipsis_color,
|
||||||
|
ellipsis_icon: None,
|
||||||
|
prev_page_ellipsis_icon: None,
|
||||||
line_breaking: LineBreaking::BreakAtWhitespace,
|
line_breaking: LineBreaking::BreakAtWhitespace,
|
||||||
page_breaking: PageBreaking::CutAndInsertEllipsis,
|
page_breaking: PageBreaking::CutAndInsertEllipsis,
|
||||||
}
|
}
|
||||||
@ -93,6 +107,34 @@ impl TextStyle {
|
|||||||
self.page_breaking = page_breaking;
|
self.page_breaking = page_breaking;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adding optional icon shown instead of "..." ellipsis.
|
||||||
|
pub const fn with_ellipsis_icon(mut self, icon: Icon) -> Self {
|
||||||
|
self.ellipsis_icon = Some(icon);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adding optional icon signalling content continues from previous page.
|
||||||
|
pub const fn with_prev_page_icon(mut self, icon: Icon) -> Self {
|
||||||
|
self.prev_page_ellipsis_icon = Some(icon);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ellipsis_width(&self) -> i16 {
|
||||||
|
if let Some(icon) = self.ellipsis_icon {
|
||||||
|
icon.toif.width()
|
||||||
|
} else {
|
||||||
|
self.text_font.text_width(ELLIPSIS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_page_ellipsis_width(&self) -> i16 {
|
||||||
|
if let Some(icon) = self.prev_page_ellipsis_icon {
|
||||||
|
icon.toif.width()
|
||||||
|
} else {
|
||||||
|
self.text_font.text_width(ELLIPSIS)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextLayout {
|
impl TextLayout {
|
||||||
@ -105,6 +147,7 @@ impl TextLayout {
|
|||||||
padding_bottom: 0,
|
padding_bottom: 0,
|
||||||
style,
|
style,
|
||||||
align: Alignment::Start,
|
align: Alignment::Start,
|
||||||
|
continues_from_prev_page: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,13 +235,31 @@ impl TextLayout {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw the arrow icon if we are in the middle of a string
|
||||||
|
if matches!(
|
||||||
|
self.style.page_breaking,
|
||||||
|
PageBreaking::CutAndInsertEllipsisBoth
|
||||||
|
) && self.continues_from_prev_page
|
||||||
|
{
|
||||||
|
sink.prev_page_ellipsis(*cursor, self);
|
||||||
|
cursor.x += self.style.prev_page_ellipsis_width();
|
||||||
|
}
|
||||||
|
|
||||||
while !remaining_text.is_empty() {
|
while !remaining_text.is_empty() {
|
||||||
|
let is_last_line = cursor.y + self.style.text_font.line_height() > self.bottom_y();
|
||||||
|
let line_ending_space = if is_last_line {
|
||||||
|
self.style.ellipsis_width()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
let remaining_width = self.bounds.x1 - cursor.x;
|
let remaining_width = self.bounds.x1 - cursor.x;
|
||||||
let span = Span::fit_horizontally(
|
let span = Span::fit_horizontally(
|
||||||
remaining_text,
|
remaining_text,
|
||||||
remaining_width,
|
remaining_width,
|
||||||
self.style.text_font,
|
self.style.text_font,
|
||||||
self.style.line_breaking,
|
self.style.line_breaking,
|
||||||
|
line_ending_space,
|
||||||
);
|
);
|
||||||
|
|
||||||
cursor.x += match self.align {
|
cursor.x += match self.align {
|
||||||
@ -228,9 +289,11 @@ impl TextLayout {
|
|||||||
if !remaining_text.is_empty() {
|
if !remaining_text.is_empty() {
|
||||||
// Append ellipsis to indicate more content is available, but only if we
|
// Append ellipsis to indicate more content is available, but only if we
|
||||||
// haven't already appended a hyphen.
|
// haven't already appended a hyphen.
|
||||||
let should_append_ellipsis =
|
let should_append_ellipsis = matches!(
|
||||||
matches!(self.style.page_breaking, PageBreaking::CutAndInsertEllipsis)
|
self.style.page_breaking,
|
||||||
&& !span.insert_hyphen_before_line_break;
|
PageBreaking::CutAndInsertEllipsis
|
||||||
|
| PageBreaking::CutAndInsertEllipsisBoth
|
||||||
|
) && !span.insert_hyphen_before_line_break;
|
||||||
if should_append_ellipsis {
|
if should_append_ellipsis {
|
||||||
sink.ellipsis(*cursor, self);
|
sink.ellipsis(*cursor, self);
|
||||||
}
|
}
|
||||||
@ -270,6 +333,10 @@ impl TextLayout {
|
|||||||
+ (end_cursor.y - init_cursor.y)
|
+ (end_cursor.y - init_cursor.y)
|
||||||
+ self.padding_bottom
|
+ self.padding_bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bottom_y(&self) -> i16 {
|
||||||
|
(self.bounds.y1 - self.padding_bottom).max(self.bounds.y0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dimensions for TextLayout {
|
impl Dimensions for TextLayout {
|
||||||
@ -303,6 +370,7 @@ pub trait LayoutSink {
|
|||||||
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {}
|
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {}
|
||||||
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||||
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||||
|
fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||||
fn line_break(&mut self, _cursor: Point) {}
|
fn line_break(&mut self, _cursor: Point) {}
|
||||||
fn out_of_bounds(&mut self) {}
|
fn out_of_bounds(&mut self) {}
|
||||||
}
|
}
|
||||||
@ -335,13 +403,41 @@ impl LayoutSink for TextRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) {
|
fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) {
|
||||||
display::text(
|
if let Some(icon) = layout.style.ellipsis_icon {
|
||||||
cursor,
|
icon.draw(
|
||||||
"...",
|
cursor,
|
||||||
layout.style.text_font,
|
BOTTOM_LEFT,
|
||||||
layout.style.ellipsis_color,
|
layout.style.ellipsis_color,
|
||||||
layout.style.background_color,
|
layout.style.background_color,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
display::text(
|
||||||
|
cursor,
|
||||||
|
ELLIPSIS,
|
||||||
|
layout.style.text_font,
|
||||||
|
layout.style.ellipsis_color,
|
||||||
|
layout.style.background_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_page_ellipsis(&mut self, cursor: Point, layout: &TextLayout) {
|
||||||
|
if let Some(icon) = layout.style.prev_page_ellipsis_icon {
|
||||||
|
icon.draw(
|
||||||
|
cursor,
|
||||||
|
BOTTOM_LEFT,
|
||||||
|
layout.style.ellipsis_color,
|
||||||
|
layout.style.background_color,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
display::text(
|
||||||
|
cursor,
|
||||||
|
ELLIPSIS,
|
||||||
|
layout.style.text_font,
|
||||||
|
layout.style.ellipsis_color,
|
||||||
|
layout.style.background_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,6 +462,10 @@ pub mod trace {
|
|||||||
self.0.string("...");
|
self.0.string("...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {
|
||||||
|
self.0.string("...");
|
||||||
|
}
|
||||||
|
|
||||||
fn line_break(&mut self, _cursor: Point) {
|
fn line_break(&mut self, _cursor: Point) {
|
||||||
self.0.string("\n");
|
self.0.string("\n");
|
||||||
}
|
}
|
||||||
@ -425,6 +525,7 @@ impl Span {
|
|||||||
max_width: i16,
|
max_width: i16,
|
||||||
text_font: impl GlyphMetrics,
|
text_font: impl GlyphMetrics,
|
||||||
breaking: LineBreaking,
|
breaking: LineBreaking,
|
||||||
|
line_ending_space: i16,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
const ASCII_LF: char = '\n';
|
const ASCII_LF: char = '\n';
|
||||||
const ASCII_CR: char = '\r';
|
const ASCII_CR: char = '\r';
|
||||||
@ -435,12 +536,31 @@ impl Span {
|
|||||||
ch == ASCII_SPACE || ch == ASCII_LF || ch == ASCII_CR
|
ch == ASCII_SPACE || ch == ASCII_LF || ch == ASCII_CR
|
||||||
}
|
}
|
||||||
|
|
||||||
let use_hyphens = !matches!(breaking, LineBreaking::BreakWordsNoHyphen);
|
// Checking if the trimmed text fits the line - the whitespace is not being
|
||||||
let hyphen_width = if use_hyphens {
|
// drawn, so we don't need to account for it.
|
||||||
|
let fits_completely = text_font.text_width(text.trim_end()) <= max_width;
|
||||||
|
let mut use_hyphens = !matches!(breaking, LineBreaking::BreakWordsNoHyphen);
|
||||||
|
|
||||||
|
// How much space we need to left unused at the end of the line
|
||||||
|
// (e.g. for the line-ending hyphen or page-ending ellipsis).
|
||||||
|
// Differs for incomplete and complete words (incomplete need
|
||||||
|
// to account for a possible hyphen).
|
||||||
|
let incomplete_word_end_width = if fits_completely {
|
||||||
|
use_hyphens = false;
|
||||||
|
0
|
||||||
|
} else if line_ending_space > 0 {
|
||||||
|
use_hyphens = false;
|
||||||
|
line_ending_space
|
||||||
|
} else if use_hyphens {
|
||||||
text_font.char_width(ASCII_HYPHEN)
|
text_font.char_width(ASCII_HYPHEN)
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
let complete_word_end_width = if fits_completely {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
line_ending_space
|
||||||
|
};
|
||||||
|
|
||||||
// The span we return in case the line has to break. We mutate it in the
|
// The span we return in case the line has to break. We mutate it in the
|
||||||
// possible break points, and its initial value is returned in case no text
|
// possible break points, and its initial value is returned in case no text
|
||||||
@ -463,7 +583,7 @@ impl Span {
|
|||||||
let char_width = text_font.char_width(ch);
|
let char_width = text_font.char_width(ch);
|
||||||
|
|
||||||
// Consider if we could be breaking the line at this position.
|
// Consider if we could be breaking the line at this position.
|
||||||
if is_whitespace(ch) {
|
if is_whitespace(ch) && span_width + complete_word_end_width <= max_width {
|
||||||
// Break before the whitespace, without hyphen.
|
// Break before the whitespace, without hyphen.
|
||||||
line.length = i;
|
line.length = i;
|
||||||
line.advance.x = span_width;
|
line.advance.x = span_width;
|
||||||
@ -483,7 +603,8 @@ impl Span {
|
|||||||
// Return the last breakpoint.
|
// Return the last breakpoint.
|
||||||
return line;
|
return line;
|
||||||
} else {
|
} else {
|
||||||
let have_space_for_break = span_width + char_width + hyphen_width <= max_width;
|
let have_space_for_break =
|
||||||
|
span_width + char_width + incomplete_word_end_width <= max_width;
|
||||||
let can_break_word =
|
let can_break_word =
|
||||||
!matches!(breaking, LineBreaking::BreakAtWhitespace) || !found_any_whitespace;
|
!matches!(breaking, LineBreaking::BreakAtWhitespace) || !found_any_whitespace;
|
||||||
if have_space_for_break && can_break_word {
|
if have_space_for_break && can_break_word {
|
||||||
@ -528,6 +649,10 @@ mod tests {
|
|||||||
fn line_height(&self) -> i16 {
|
fn line_height(&self) -> i16 {
|
||||||
self.height
|
self.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_width(&self, text: &str) -> i16 {
|
||||||
|
self.width * text.len() as i16
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FIXED_FONT: Fixed = Fixed {
|
const FIXED_FONT: Fixed = Fixed {
|
||||||
@ -591,6 +716,7 @@ mod tests {
|
|||||||
max_width,
|
max_width,
|
||||||
FIXED_FONT,
|
FIXED_FONT,
|
||||||
LineBreaking::BreakAtWhitespace,
|
LineBreaking::BreakAtWhitespace,
|
||||||
|
0,
|
||||||
);
|
);
|
||||||
spans.push((
|
spans.push((
|
||||||
&remaining_text[..span.length],
|
&remaining_text[..span.length],
|
||||||
|
@ -392,6 +392,9 @@ impl PageOffset {
|
|||||||
|
|
||||||
// Find out the dimensions of the paragraph at given char offset.
|
// Find out the dimensions of the paragraph at given char offset.
|
||||||
let mut layout = paragraph.layout(area);
|
let mut layout = paragraph.layout(area);
|
||||||
|
if self.chr > 0 {
|
||||||
|
layout.continues_from_prev_page = true;
|
||||||
|
}
|
||||||
let fit = layout.fit_text(paragraph.content.as_ref());
|
let fit = layout.fit_text(paragraph.content.as_ref());
|
||||||
let (used, remaining_area) = area.split_top(fit.height());
|
let (used, remaining_area) = area.split_top(fit.height());
|
||||||
layout.bounds = used;
|
layout.bounds = used;
|
||||||
|
@ -114,9 +114,12 @@ pub struct Icon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
pub fn new(data: &'static [u8]) -> Self {
|
pub const fn new(data: &'static [u8]) -> Self {
|
||||||
let toif = unwrap!(Toif::new(data));
|
let toif = match Toif::new(data) {
|
||||||
assert!(toif.format() == ToifFormat::GrayScaleEH);
|
Some(t) => t,
|
||||||
|
None => panic!("Invalid image."),
|
||||||
|
};
|
||||||
|
assert!(matches!(toif.format(), ToifFormat::GrayScaleEH));
|
||||||
Self { toif }
|
Self { toif }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,8 +497,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
page.place(SCREEN);
|
page.place(SCREEN);
|
||||||
|
|
||||||
let expected1 = "<SwipePage active_page:0 page_count:2 content:<Paragraphs This is somewhat long\nparagraph that goes on\nand on and on and on\nand on and will definitely\nnot fit on just a single\nscreen. You have to\nswipe a bit to see all the\ntext it contains I guess....\n> buttons:<FixedHeightBar inner:<Button text:NO > > >";
|
let expected1 = "<SwipePage active_page:0 page_count:2 content:<Paragraphs This is somewhat long\nparagraph that goes on\nand on and on and on\nand on and will definitely\nnot fit on just a single\nscreen. You have to\nswipe a bit to see all the\ntext it contains I...\n> buttons:<FixedHeightBar inner:<Button text:NO > > >";
|
||||||
let expected2 = "<SwipePage active_page:1 page_count:2 content:<Paragraphs There's just so much\nletters in it.\n> buttons:<FixedHeightBar inner:<Button text:NO > > >";
|
let expected2 = "<SwipePage active_page:1 page_count:2 content:<Paragraphs guess. There's just so\nmuch letters in it.\n> buttons:<FixedHeightBar inner:<Button text:NO > > >";
|
||||||
|
|
||||||
assert_eq!(trace(&page), expected1);
|
assert_eq!(trace(&page), expected1);
|
||||||
swipe_down(&mut page);
|
swipe_down(&mut page);
|
||||||
@ -533,9 +533,9 @@ mod tests {
|
|||||||
);
|
);
|
||||||
page.place(SCREEN);
|
page.place(SCREEN);
|
||||||
|
|
||||||
let expected1 = "<SwipePage active_page:0 page_count:3 content:<Paragraphs This paragraph is using a\nbold font. It doesn't\nneed to be all that long.\nAnd this one is\nusing MONO. Mono\nspace is nice fo\nr numbers, they\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
let expected1 = "<SwipePage active_page:0 page_count:3 content:<Paragraphs This paragraph is using a\nbold font. It doesn't\nneed to be all that long.\nAnd this one is\nusing MONO. Mono\nspace is nice fo\nr numbers,...\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
||||||
let expected2 = "<SwipePage active_page:1 page_count:3 content:<Paragraphs have the same wi\ndth and can be s\ncanned quickly.\nEven if they spa\nn several pages\nor something.\nLet's add another one...\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
let expected2 = "<SwipePage active_page:1 page_count:3 content:<Paragraphs ...they have th\ne same width and\ncan be scanned q\nuickly. Even if\nthey span severa\nl pages or somet\nhing.\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
||||||
let expected3 = "<SwipePage active_page:2 page_count:3 content:<Paragraphs for a good measure. This\none should overflow all\nthe way to the third\npage with a bit of luck.\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
let expected3 = "<SwipePage active_page:2 page_count:3 content:<Paragraphs Let's add another one\nfor a good measure. This\none should overflow all\nthe way to the third\npage with a bit of luck.\n> buttons:<FixedHeightBar inner:<Button text:IDK > > >";
|
||||||
|
|
||||||
assert_eq!(trace(&page), expected1);
|
assert_eq!(trace(&page), expected1);
|
||||||
swipe_down(&mut page);
|
swipe_down(&mut page);
|
||||||
|
BIN
core/embed/rust/src/ui/model_tt/res/page-next.toif
Normal file
BIN
core/embed/rust/src/ui/model_tt/res/page-next.toif
Normal file
Binary file not shown.
BIN
core/embed/rust/src/ui/model_tt/res/page-prev.toif
Normal file
BIN
core/embed/rust/src/ui/model_tt/res/page-prev.toif
Normal file
Binary file not shown.
@ -5,7 +5,7 @@ use crate::{
|
|||||||
text::{formatted::FormattedFonts, LineBreaking, PageBreaking, TextStyle},
|
text::{formatted::FormattedFonts, LineBreaking, PageBreaking, TextStyle},
|
||||||
FixedHeightBar,
|
FixedHeightBar,
|
||||||
},
|
},
|
||||||
display::{Color, Font},
|
display::{Color, Font, Icon},
|
||||||
geometry::Insets,
|
geometry::Insets,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -68,6 +68,8 @@ pub const ICON_LOCK: &[u8] = include_res!("model_tt/res/lock.toif");
|
|||||||
pub const ICON_LOGO: &[u8] = include_res!("model_tt/res/logo.toif");
|
pub const ICON_LOGO: &[u8] = include_res!("model_tt/res/logo.toif");
|
||||||
pub const ICON_SUCCESS_SMALL: &[u8] = include_res!("model_tt/res/success_bld.toif");
|
pub const ICON_SUCCESS_SMALL: &[u8] = include_res!("model_tt/res/success_bld.toif");
|
||||||
pub const ICON_WARN_SMALL: &[u8] = include_res!("model_tt/res/warn_bld.toif");
|
pub const ICON_WARN_SMALL: &[u8] = include_res!("model_tt/res/warn_bld.toif");
|
||||||
|
pub const ICON_PAGE_NEXT: &[u8] = include_res!("model_tt/res/page-next.toif");
|
||||||
|
pub const ICON_PAGE_PREV: &[u8] = include_res!("model_tt/res/page-prev.toif");
|
||||||
|
|
||||||
// Large, three-color icons.
|
// Large, three-color icons.
|
||||||
pub const WARN_COLOR: Color = YELLOW;
|
pub const WARN_COLOR: Color = YELLOW;
|
||||||
@ -389,7 +391,9 @@ pub const TEXT_DEMIBOLD: TextStyle = TextStyle::new(Font::DEMIBOLD, FG, BG, GREY
|
|||||||
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
|
pub const TEXT_BOLD: TextStyle = TextStyle::new(Font::BOLD, FG, BG, GREY_LIGHT, GREY_LIGHT);
|
||||||
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, GREY_LIGHT, GREY_LIGHT)
|
pub const TEXT_MONO: TextStyle = TextStyle::new(Font::MONO, FG, BG, GREY_LIGHT, GREY_LIGHT)
|
||||||
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
.with_line_breaking(LineBreaking::BreakWordsNoHyphen)
|
||||||
.with_page_breaking(PageBreaking::Cut);
|
.with_page_breaking(PageBreaking::CutAndInsertEllipsisBoth)
|
||||||
|
.with_ellipsis_icon(Icon::new(ICON_PAGE_NEXT))
|
||||||
|
.with_prev_page_icon(Icon::new(ICON_PAGE_PREV));
|
||||||
|
|
||||||
/// Convert Python-side numeric id to a `TextStyle`.
|
/// Convert Python-side numeric id to a `TextStyle`.
|
||||||
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
pub fn textstyle_number(num: i32) -> &'static TextStyle {
|
||||||
|
Loading…
Reference in New Issue
Block a user