From 8c47b94b79d409c0cf3c822521c6a71b509ba4ec Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Sun, 21 Nov 2021 21:52:50 +0100 Subject: [PATCH] feat(core/rust): Add experimental LinearLayout --- core/embed/rust/src/ui/component/label.rs | 28 ++--- core/embed/rust/src/ui/geometry.rs | 124 +++++++++++++++++++++- 2 files changed, 135 insertions(+), 17 deletions(-) diff --git a/core/embed/rust/src/ui/component/label.rs b/core/embed/rust/src/ui/component/label.rs index d85e00c3a..b64477ae0 100644 --- a/core/embed/rust/src/ui/component/label.rs +++ b/core/embed/rust/src/ui/component/label.rs @@ -3,7 +3,7 @@ use core::ops::Deref; use crate::ui::{ component::{Component, Event, EventCtx, Never}, display::{self, Color, Font}, - geometry::{Align, Point, Rect}, + geometry::{Alignment, Point, Rect}, }; pub struct LabelStyle { @@ -22,45 +22,45 @@ impl Label where T: Deref, { - pub fn new(origin: Point, align: Align, text: T, style: LabelStyle) -> Self { + pub fn new(origin: Point, align: Alignment, text: T, style: LabelStyle) -> Self { let width = display::text_width(&text, style.font); let height = display::line_height(); let area = match align { // `origin` is the top-left point. - Align::Left => Rect { + Alignment::Start => Rect { x0: origin.x, y0: origin.y, x1: origin.x + width, y1: origin.y + height, }, - // `origin` is the top-right point. - Align::Right => Rect { - x0: origin.x - width, - y0: origin.y, - x1: origin.x, - y1: origin.y + height, - }, // `origin` is the top-centered point. - Align::Center => Rect { + Alignment::Center => Rect { x0: origin.x - width / 2, y0: origin.y, x1: origin.x + width / 2, y1: origin.y + height, }, + // `origin` is the top-right point. + Alignment::End => Rect { + x0: origin.x - width, + y0: origin.y, + x1: origin.x, + y1: origin.y + height, + }, }; Self { area, style, text } } pub fn left_aligned(origin: Point, text: T, style: LabelStyle) -> Self { - Self::new(origin, Align::Left, text, style) + Self::new(origin, Alignment::Start, text, style) } pub fn right_aligned(origin: Point, text: T, style: LabelStyle) -> Self { - Self::new(origin, Align::Right, text, style) + Self::new(origin, Alignment::End, text, style) } pub fn centered(origin: Point, text: T, style: LabelStyle) -> Self { - Self::new(origin, Align::Center, text, style) + Self::new(origin, Alignment::Center, text, style) } pub fn text(&self) -> &T { diff --git a/core/embed/rust/src/ui/geometry.rs b/core/embed/rust/src/ui/geometry.rs index 83a89a87e..4546fc174 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -22,6 +22,13 @@ impl Offset { Self::new(0, 0) } + pub fn on_axis(axis: Axis, a: i32) -> Self { + match axis { + Axis::Horizontal => Self::new(a, 0), + Axis::Vertical => Self::new(0, a), + } + } + pub fn abs(self) -> Self { Self::new(self.x.abs(), self.y.abs()) } @@ -109,6 +116,10 @@ impl Rect { } } + pub const fn zero() -> Self { + Self::new(Point::zero(), Point::zero()) + } + pub fn from_top_left_and_size(p0: Point, size: Offset) -> Self { Self::new(p0, p0 + size) } @@ -231,10 +242,32 @@ impl Rect { } #[derive(Copy, Clone, PartialEq, Eq)] -pub enum Align { - Left, - Right, +pub enum Alignment { + Start, Center, + End, +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum Axis { + Horizontal, + Vertical, +} + +impl Axis { + pub fn main(self, x: T, y: T) -> T { + match self { + Axis::Horizontal => x, + Axis::Vertical => y, + } + } + + pub fn cross(self) -> Self { + match self { + Axis::Horizontal => Axis::Vertical, + Axis::Vertical => Axis::Horizontal, + } + } } pub struct Grid { @@ -275,3 +308,88 @@ impl Grid { self.row_col(index / self.cols, index % self.cols) } } + +#[derive(Copy, Clone)] +pub struct LinearLayout { + axis: Axis, + align: Alignment, + spacing: i32, +} + +impl LinearLayout { + pub fn horizontal() -> Self { + Self { + axis: Axis::Horizontal, + align: Alignment::Start, + spacing: 0, + } + } + + pub fn vertical() -> Self { + Self { + axis: Axis::Vertical, + align: Alignment::Start, + spacing: 0, + } + } + + pub fn align_at_start(mut self) -> Self { + self.align = Alignment::Start; + self + } + + pub fn align_at_center(mut self) -> Self { + self.align = Alignment::Center; + self + } + + pub fn align_at_end(mut self) -> Self { + self.align = Alignment::End; + self + } + + pub fn with_spacing(mut self, spacing: i32) -> Self { + self.spacing = spacing; + self + } + + /// Arranges all `items` by parameters configured in `self` into `area`. + /// Does not change the size of the items (only the position), but it needs + /// to iterate (and ask for the size) twice. + pub fn arrange(&self, area: Rect, items: &mut [impl Dimensions]) { + let item_sum: i32 = items + .iter_mut() + .map(|i| { + let size = i.get_size(); + self.axis.main(size.x, size.y) + }) + .sum(); + let spacing_count = items.len().saturating_sub(1); + let spacing_sum = spacing_count as i32 * self.spacing; + let total_size = item_sum + spacing_sum; + + let available_space = match self.axis { + Axis::Horizontal => area.width(), + Axis::Vertical => area.height(), + }; + let mut cursor = match self.align { + Alignment::Start => 0, + Alignment::Center => available_space / 2 - total_size / 2, + Alignment::End => available_space - total_size, + }; + + for item in items { + let top_left = area.top_left() + Offset::on_axis(self.axis, cursor); + let size = item.get_size(); + item.set_area(Rect::from_top_left_and_size(top_left, size)); + cursor += self.axis.main(size.x, size.y); + cursor += self.spacing; + } + } +} + +/// Types that have a size and a position. +pub trait Dimensions { + fn get_size(&mut self) -> Offset; + fn set_area(&mut self, area: Rect); +}