mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-08-05 13:26:57 +00:00
rust: Layout docs and improvements
This commit is contained in:
parent
3c03645060
commit
9abecbdd9b
@ -29,7 +29,9 @@ pub enum PageMsg<T, U> {
|
||||
}
|
||||
|
||||
pub trait Paginate {
|
||||
/// How many pages of content are there in total?
|
||||
fn page_count(&mut self) -> usize;
|
||||
/// Navigate to the given page.
|
||||
fn change_page(&mut self, active_page: usize);
|
||||
}
|
||||
|
||||
|
@ -54,9 +54,9 @@ pub struct TextLayout {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TextStyle {
|
||||
/// Text font ID. Can be overridden by `Op::Font`.
|
||||
/// Text font ID.
|
||||
pub text_font: Font,
|
||||
/// Text color. Can be overridden by `Op::Color`.
|
||||
/// Text color.
|
||||
pub text_color: Color,
|
||||
/// Background color.
|
||||
pub background_color: Color,
|
||||
@ -168,20 +168,28 @@ impl TextLayout {
|
||||
self
|
||||
}
|
||||
|
||||
/// Baseline `Point` where we are starting to draw the text.
|
||||
pub fn initial_cursor(&self) -> Point {
|
||||
let font = &self.style.text_font;
|
||||
self.bounds.top_left()
|
||||
+ Offset::y(font.text_max_height() - font.text_baseline() + self.padding_top)
|
||||
}
|
||||
|
||||
/// Trying to fit the content on the current screen.
|
||||
pub fn fit_text(&self, text: &str) -> LayoutFit {
|
||||
self.layout_text(text, &mut self.initial_cursor(), &mut TextNoOp)
|
||||
}
|
||||
|
||||
/// Draw as much text as possible on the current screen.
|
||||
pub fn render_text(&self, text: &str) {
|
||||
self.layout_text(text, &mut self.initial_cursor(), &mut TextRenderer);
|
||||
}
|
||||
|
||||
/// Y coordinate of the bottom of the available space/bounds
|
||||
pub fn bottom_y(&self) -> i16 {
|
||||
(self.bounds.y1 - self.padding_bottom).max(self.bounds.y0)
|
||||
}
|
||||
|
||||
pub fn layout_ops<'o>(
|
||||
mut self,
|
||||
ops: &mut dyn Iterator<Item = Op<'o>>,
|
||||
@ -225,6 +233,9 @@ impl TextLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/// Loop through the `text` and try to fit it on the current screen,
|
||||
/// reporting events to `sink`, which may do something with them (e.g. draw
|
||||
/// on screen).
|
||||
pub fn layout_text(
|
||||
&self,
|
||||
text: &str,
|
||||
@ -232,11 +243,10 @@ impl TextLayout {
|
||||
sink: &mut dyn LayoutSink,
|
||||
) -> LayoutFit {
|
||||
let init_cursor = *cursor;
|
||||
let bottom = (self.bounds.y1 - self.padding_bottom).max(self.bounds.y0);
|
||||
let mut remaining_text = text;
|
||||
|
||||
// Check if bounding box is high enough for at least one line.
|
||||
if cursor.y > bottom {
|
||||
if cursor.y > self.bottom_y() {
|
||||
sink.out_of_bounds();
|
||||
return LayoutFit::OutOfBounds {
|
||||
processed_chars: 0,
|
||||
@ -251,6 +261,7 @@ impl TextLayout {
|
||||
) && self.continues_from_prev_page
|
||||
{
|
||||
sink.prev_page_ellipsis(*cursor, self);
|
||||
// Move the cursor to the right, always the same distance
|
||||
cursor.x += self.style.prev_page_ellipsis_width();
|
||||
}
|
||||
|
||||
@ -278,7 +289,12 @@ impl TextLayout {
|
||||
};
|
||||
|
||||
// Report the span at the cursor position.
|
||||
sink.text(*cursor, self, &remaining_text[..span.length]);
|
||||
// Not doing it when the span length is 0, as that
|
||||
// means we encountered a newline/line-break, which we do not draw.
|
||||
// Line-breaks are reported later.
|
||||
if span.length > 0 {
|
||||
sink.text(*cursor, self, &remaining_text[..span.length]);
|
||||
}
|
||||
|
||||
// Continue with the rest of the remaining_text.
|
||||
remaining_text = &remaining_text[span.length + span.skip_next_chars..];
|
||||
@ -294,7 +310,8 @@ impl TextLayout {
|
||||
sink.hyphen(*cursor, self);
|
||||
}
|
||||
// Check the amount of vertical space we have left.
|
||||
if cursor.y + span.advance.y > bottom {
|
||||
if cursor.y + span.advance.y > self.bottom_y() {
|
||||
// Not enough space on this page.
|
||||
if !remaining_text.is_empty() {
|
||||
// Append ellipsis to indicate more content is available, but only if we
|
||||
// haven't already appended a hyphen.
|
||||
@ -336,16 +353,13 @@ impl TextLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/// Overall height of the content, including paddings.
|
||||
fn layout_height(&self, init_cursor: Point, end_cursor: Point) -> i16 {
|
||||
self.padding_top
|
||||
+ self.style.text_font.text_height()
|
||||
+ (end_cursor.y - init_cursor.y)
|
||||
+ self.padding_bottom
|
||||
}
|
||||
|
||||
fn bottom_y(&self) -> i16 {
|
||||
(self.bounds.y1 - self.padding_bottom).max(self.bounds.y0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dimensions for TextLayout {
|
||||
@ -358,6 +372,8 @@ impl Dimensions for TextLayout {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether we can fit content on the current screen.
|
||||
/// Knows how many characters got processed and how high the content is.
|
||||
pub enum LayoutFit {
|
||||
/// Entire content fits. Vertical size is returned in `height`.
|
||||
Fitting { processed_chars: usize, height: i16 },
|
||||
@ -366,6 +382,7 @@ pub enum LayoutFit {
|
||||
}
|
||||
|
||||
impl LayoutFit {
|
||||
/// How high is the processed/fitted content.
|
||||
pub fn height(&self) -> i16 {
|
||||
match self {
|
||||
LayoutFit::Fitting { height, .. } => *height,
|
||||
@ -375,24 +392,36 @@ impl LayoutFit {
|
||||
}
|
||||
|
||||
/// Visitor for text segment operations.
|
||||
/// Defines responses for certain kind of events encountered
|
||||
/// when processing the content.
|
||||
pub trait LayoutSink {
|
||||
/// Text should be processed.
|
||||
fn text(&mut self, _cursor: Point, _layout: &TextLayout, _text: &str) {}
|
||||
/// Hyphen at the end of line.
|
||||
fn hyphen(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||
/// Ellipsis at the end of the page.
|
||||
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||
/// Ellipsis at the beginning of the page.
|
||||
fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {}
|
||||
/// Line break - a newline.
|
||||
fn line_break(&mut self, _cursor: Point) {}
|
||||
/// Content cannot fit on the screen.
|
||||
fn out_of_bounds(&mut self) {}
|
||||
}
|
||||
|
||||
/// `LayoutSink` without any functionality.
|
||||
/// Used to consume events when counting pages
|
||||
/// or navigating to a certain page number.
|
||||
pub struct TextNoOp;
|
||||
|
||||
impl LayoutSink for TextNoOp {}
|
||||
|
||||
/// `LayoutSink` for rendering the content.
|
||||
pub struct TextRenderer;
|
||||
|
||||
impl LayoutSink for TextRenderer {
|
||||
fn text(&mut self, cursor: Point, layout: &TextLayout, text: &str) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
text,
|
||||
layout.style.text_font,
|
||||
@ -402,7 +431,7 @@ impl LayoutSink for TextRenderer {
|
||||
}
|
||||
|
||||
fn hyphen(&mut self, cursor: Point, layout: &TextLayout) {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
"-",
|
||||
layout.style.text_font,
|
||||
@ -413,14 +442,15 @@ impl LayoutSink for TextRenderer {
|
||||
|
||||
fn ellipsis(&mut self, cursor: Point, layout: &TextLayout) {
|
||||
if let Some(icon) = layout.style.ellipsis_icon {
|
||||
let bottom_left = cursor + Offset::new(layout.style.ellipsis_icon_margin, 1);
|
||||
icon.draw(
|
||||
cursor,
|
||||
bottom_left,
|
||||
BOTTOM_LEFT,
|
||||
layout.style.ellipsis_color,
|
||||
layout.style.background_color,
|
||||
);
|
||||
} else {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
ELLIPSIS,
|
||||
layout.style.text_font,
|
||||
@ -439,7 +469,7 @@ impl LayoutSink for TextRenderer {
|
||||
layout.style.background_color,
|
||||
);
|
||||
} else {
|
||||
display::text(
|
||||
display::text_left(
|
||||
cursor,
|
||||
ELLIPSIS,
|
||||
layout.style.text_font,
|
||||
@ -456,6 +486,7 @@ pub mod trace {
|
||||
|
||||
use super::*;
|
||||
|
||||
/// `LayoutSink` for debugging purposes.
|
||||
pub struct TraceSink<'a>(pub &'a mut dyn crate::trace::Tracer);
|
||||
|
||||
impl<'a> LayoutSink for TraceSink<'a> {
|
||||
@ -468,11 +499,11 @@ pub mod trace {
|
||||
}
|
||||
|
||||
fn ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {
|
||||
self.0.string("...");
|
||||
self.0.string(ELLIPSIS);
|
||||
}
|
||||
|
||||
fn prev_page_ellipsis(&mut self, _cursor: Point, _layout: &TextLayout) {
|
||||
self.0.string("...");
|
||||
self.0.string(ELLIPSIS);
|
||||
}
|
||||
|
||||
fn line_break(&mut self, _cursor: Point) {
|
||||
@ -513,6 +544,8 @@ impl<'a> Op<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Carries info about the content that was processed
|
||||
/// on the current line.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
/// How many characters from the input text this span is laying out.
|
||||
@ -609,7 +642,7 @@ impl Span {
|
||||
}
|
||||
found_any_whitespace = true;
|
||||
} else if span_width + char_width > max_width {
|
||||
// Return the last breakpoint.
|
||||
// Cannot fit on this line. Return the last breakpoint.
|
||||
return line;
|
||||
} else {
|
||||
let have_space_for_break =
|
||||
@ -631,7 +664,7 @@ impl Span {
|
||||
span_width += char_width;
|
||||
}
|
||||
|
||||
// The whole text is fitting.
|
||||
// The whole text is fitting on the current line.
|
||||
Self {
|
||||
length: text.len(),
|
||||
advance: Offset::x(span_width),
|
||||
|
Loading…
Reference in New Issue
Block a user