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:
parent
247d9d267a
commit
f0d5f9069a
@ -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]);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>,
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user