1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-01-23 05:40:57 +00:00

refactor(core/rust/ui): text centering in Paragraphs

Only works for paragraphs, not formatted text.

[no changelog]
This commit is contained in:
Martin Milata 2022-07-03 16:24:06 +02:00
parent 247d9d267a
commit f0d5f9069a
4 changed files with 62 additions and 32 deletions

View File

@ -2,7 +2,7 @@ use super::iter::GlyphMetrics;
use crate::ui::{
display,
display::{Color, Font},
geometry::{Offset, Point, Rect},
geometry::{Alignment, Offset, Point, Rect},
};
#[derive(Copy, Clone)]
@ -39,6 +39,8 @@ pub struct TextLayout {
/// Fonts, colors, line/page breaking behavior.
pub style: TextStyle,
/// Horizontal alignment.
pub align: Alignment,
}
#[derive(Copy, Clone)]
@ -90,6 +92,7 @@ impl TextLayout {
padding_top: 0,
padding_bottom: 0,
style,
align: Alignment::Start,
}
}
@ -98,6 +101,11 @@ impl TextLayout {
self
}
pub fn with_align(mut self, align: Alignment) -> Self {
self.align = align;
self
}
pub fn initial_cursor(&self) -> Point {
self.bounds.top_left() + Offset::y(self.style.text_font.text_height() + self.padding_top)
}
@ -173,13 +181,20 @@ impl TextLayout {
}
while !remaining_text.is_empty() {
let remaining_width = self.bounds.x1 - cursor.x;
let span = Span::fit_horizontally(
remaining_text,
self.bounds.x1 - cursor.x,
remaining_width,
self.style.text_font,
self.style.line_breaking,
);
cursor.x += match self.align {
Alignment::Start => 0,
Alignment::Center => (remaining_width - span.advance.x) / 2,
Alignment::End => remaining_width - span.advance.x,
};
// Report the span at the cursor position.
sink.text(*cursor, self, &remaining_text[..span.length]);

View File

@ -2,7 +2,8 @@ use heapless::Vec;
use crate::ui::{
component::{Component, Event, EventCtx, Never, Paginate},
geometry::{Dimensions, Insets, LinearPlacement, Rect},
display::Color,
geometry::{Alignment, Dimensions, Insets, LinearPlacement, Rect},
};
use super::layout::{LayoutFit, TextLayout, TextStyle};
@ -52,6 +53,14 @@ where
self
}
pub fn add_color(self, style: TextStyle, color: Color, content: T) -> Self {
let color_style = TextStyle {
text_color: color,
..style
};
self.add(color_style, content)
}
pub fn add(mut self, style: TextStyle, content: T) -> Self {
if content.as_ref().is_empty() {
return self;
@ -71,6 +80,13 @@ where
self
}
pub fn centered(mut self) -> Self {
if let Some(ref mut para) = self.list.last_mut() {
para.layout.align = Alignment::Center;
};
self
}
pub fn add_break(mut self) -> Self {
if let Some(ref mut para) = self.list.last_mut() {
para.break_after = true;

View File

@ -1,8 +1,6 @@
use core::ops::Deref;
use crate::ui::{
component::{Child, Component, Event, EventCtx, Image, Label, Never},
geometry::{Grid, Insets, Rect},
component::{text::paragraphs::Paragraphs, Child, Component, Event, EventCtx, Image, Never},
geometry::{Grid, Insets, LinearPlacement, Rect},
};
use super::{theme, Button};
@ -100,38 +98,48 @@ where
pub struct IconDialog<T, U> {
image: Child<Image>,
title: Label<T>,
description: Option<Label<T>>,
paragraphs: Paragraphs<T>,
controls: Child<U>,
}
impl<T, U> IconDialog<T, U>
where
T: Deref<Target = str>,
T: AsRef<str>,
U: Component,
{
pub fn new(icon: &'static [u8], title: T, controls: U) -> Self {
Self {
image: Child::new(Image::new(icon)),
title: Label::centered(title, theme::label_warning()),
description: None,
paragraphs: Paragraphs::new()
.with_placement(
LinearPlacement::vertical()
.align_at_start()
.with_spacing(Self::VALUE_SPACE),
)
.add(theme::TEXT_MEDIUM, title)
.centered(),
controls: Child::new(controls),
}
}
pub fn with_description(mut self, description: T) -> Self {
self.description = Some(Label::centered(description, theme::label_warning_value()));
if !description.as_ref().is_empty() {
self.paragraphs = self
.paragraphs
.add_color(theme::TEXT_NORMAL, theme::OFF_WHITE, description)
.centered();
}
self
}
pub const ICON_AREA_HEIGHT: i32 = 64;
pub const DESCRIPTION_SPACE: i32 = 13;
pub const VALUE_SPACE: i32 = 9;
pub const DESCRIPTION_SPACE: i32 = 14;
pub const VALUE_SPACE: i32 = 5;
}
impl<T, U> Component for IconDialog<T, U>
where
T: Deref<Target = str>,
T: AsRef<str>,
U: Component,
{
type Msg = DialogMsg<Never, U::Msg>;
@ -141,33 +149,27 @@ where
let (content, buttons) = bounds.split_bottom(Button::<&str>::HEIGHT);
let (image, content) = content.split_top(Self::ICON_AREA_HEIGHT);
let content = content.inset(Insets::top(Self::DESCRIPTION_SPACE));
let (title, content) = content.split_top(self.title.size().y);
let content = content.inset(Insets::top(Self::VALUE_SPACE));
self.image.place(image);
self.title.place(title);
self.description.place(content);
self.paragraphs.place(content);
self.controls.place(buttons);
bounds
}
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
self.title.event(ctx, event);
self.description.event(ctx, event);
self.paragraphs.event(ctx, event);
self.controls.event(ctx, event).map(Self::Msg::Controls)
}
fn paint(&mut self) {
self.image.paint();
self.title.paint();
self.description.paint();
self.paragraphs.paint();
self.controls.paint();
}
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
self.image.bounds(sink);
self.title.bounds(sink);
self.description.bounds(sink);
self.paragraphs.bounds(sink);
self.controls.bounds(sink);
}
}
@ -175,15 +177,12 @@ where
#[cfg(feature = "ui_debug")]
impl<T, U> crate::trace::Trace for IconDialog<T, U>
where
T: Deref<Target = str>,
T: AsRef<str>,
U: crate::trace::Trace,
{
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.open("IconDialog");
t.field("title", &self.title);
if let Some(ref description) = self.description {
t.field("description", description);
}
t.field("content", &self.paragraphs);
t.field("controls", &self.controls);
t.close();
}

View File

@ -77,7 +77,7 @@ where
impl<T, U> ComponentMsgObj for IconDialog<T, U>
where
T: Deref<Target = str>,
T: AsRef<str>,
U: Component,
<U as Component>::Msg: TryInto<Obj, Error = Error>,
{