diff --git a/core/embed/rust/src/ui/geometry.rs b/core/embed/rust/src/ui/geometry.rs index 8062d6669..9f778d887 100644 --- a/core/embed/rust/src/ui/geometry.rs +++ b/core/embed/rust/src/ui/geometry.rs @@ -1,5 +1,31 @@ use crate::ui::lerp::Lerp; -use core::ops::{Add, Sub}; +use core::ops::{Add, Neg, Sub}; + +const fn min(a: i32, b: i32) -> i32 { + if a < b { + a + } else { + b + } +} + +const fn max(a: i32, b: i32) -> i32 { + if a > b { + a + } else { + b + } +} + +const fn clamp(x: i32, min: i32, max: i32) -> i32 { + if x < min { + min + } else if x > max { + max + } else { + x + } +} /// Relative offset in 2D space, used for representing translation and /// dimensions of objects. Absolute positions on the screen are represented by @@ -31,27 +57,27 @@ impl Offset { Self::new(0, y) } - pub fn on_axis(axis: Axis, a: i32) -> Self { + pub const fn on_axis(axis: Axis, a: i32) -> Self { match axis { Axis::Horizontal => Self::new(a, 0), Axis::Vertical => Self::new(0, a), } } - pub fn axis(&self, axis: Axis) -> i32 { + pub const fn axis(&self, axis: Axis) -> i32 { match axis { Axis::Horizontal => self.x, Axis::Vertical => self.y, } } - pub fn abs(self) -> Self { + pub const fn abs(self) -> Self { Self::new(self.x.abs(), self.y.abs()) } /// With `self` representing a rectangle size, returns top-left corner of /// the rectangle such that it is aligned relative to the `point`. - pub fn snap(self, point: Point, x: Alignment, y: Alignment) -> Point { + pub const fn snap(self, point: Point, x: Alignment, y: Alignment) -> Point { let x_off = match x { Alignment::Start => 0, Alignment::Center => self.x / 2, @@ -62,7 +88,19 @@ impl Offset { Alignment::Center => self.y / 2, Alignment::End => self.y, }; - point - Self::new(x_off, y_off) + point.ofs(Self::new(-x_off, -y_off)) + } + + pub const fn neg(self) -> Self { + Self::new(-self.x, -self.y) + } + + pub const fn add(self, rhs: Offset) -> Self { + Self::new(self.x + rhs.x, self.y + rhs.y) + } + + pub const fn sub(self, rhs: Offset) -> Self { + self.add(rhs.neg()) } } @@ -70,7 +108,15 @@ impl Add for Offset { type Output = Offset; fn add(self, rhs: Offset) -> Self::Output { - Self::new(self.x + rhs.x, self.y + rhs.y) + Offset::add(self, rhs) + } +} + +impl Neg for Offset { + type Output = Offset; + + fn neg(self) -> Self::Output { + Offset::neg(self) } } @@ -78,7 +124,7 @@ impl Sub for Offset { type Output = Offset; fn sub(self, rhs: Offset) -> Self::Output { - Self::new(self.x - rhs.x, self.y - rhs.y) + Offset::sub(self, rhs) } } @@ -99,16 +145,24 @@ impl Point { Self::new(0, 0) } - pub fn center(self, rhs: Self) -> Self { + pub const fn center(self, rhs: Self) -> Self { Self::new((self.x + rhs.x) / 2, (self.y + rhs.y) / 2) } + + pub const fn ofs(self, rhs: Offset) -> Self { + Self::new(self.x + rhs.x, self.y + rhs.y) + } + + pub const fn sub(self, rhs: Point) -> Offset { + Offset::new(self.x - rhs.x, self.y - rhs.y) + } } impl Add for Point { type Output = Point; fn add(self, rhs: Offset) -> Self::Output { - Self::new(self.x + rhs.x, self.y + rhs.y) + self.ofs(rhs) } } @@ -116,7 +170,7 @@ impl Sub for Point { type Output = Point; fn sub(self, rhs: Offset) -> Self::Output { - Self::new(self.x - rhs.x, self.y - rhs.y) + self.ofs(-rhs) } } @@ -124,7 +178,7 @@ impl Sub for Point { type Output = Offset; fn sub(self, rhs: Point) -> Self::Output { - Offset::new(self.x - rhs.x, self.y - rhs.y) + Point::sub(self, rhs) } } @@ -167,7 +221,7 @@ impl Rect { } } - pub fn from_center_and_size(p: Point, size: Offset) -> Self { + pub const fn from_center_and_size(p: Point, size: Offset) -> Self { Self { x0: p.x - size.x / 2, y0: p.y - size.y / 2, @@ -176,68 +230,68 @@ impl Rect { } } - pub fn with_top_left(self, p0: Point) -> Self { + pub const fn with_top_left(self, p0: Point) -> Self { Self::from_top_left_and_size(p0, self.size()) } - pub fn with_size(self, size: Offset) -> Self { + pub const fn with_size(self, size: Offset) -> Self { Self::from_top_left_and_size(self.top_left(), size) } - pub fn with_width(self, width: i32) -> Self { + pub const fn with_width(self, width: i32) -> Self { self.with_size(Offset::new(width, self.height())) } - pub fn with_height(self, height: i32) -> Self { + pub const fn with_height(self, height: i32) -> Self { self.with_size(Offset::new(self.width(), height)) } - pub fn width(&self) -> i32 { + pub const fn width(&self) -> i32 { self.x1 - self.x0 } - pub fn height(&self) -> i32 { + pub const fn height(&self) -> i32 { self.y1 - self.y0 } - pub fn size(&self) -> Offset { + pub const fn size(&self) -> Offset { Offset::new(self.width(), self.height()) } - pub fn top_left(&self) -> Point { + pub const fn top_left(&self) -> Point { Point::new(self.x0, self.y0) } - pub fn top_right(&self) -> Point { + pub const fn top_right(&self) -> Point { Point::new(self.x1, self.y0) } - pub fn bottom_left(&self) -> Point { + pub const fn bottom_left(&self) -> Point { Point::new(self.x0, self.y1) } - pub fn bottom_right(&self) -> Point { + pub const fn bottom_right(&self) -> Point { Point::new(self.x1, self.y1) } - pub fn center(&self) -> Point { + pub const fn center(&self) -> Point { self.top_left().center(self.bottom_right()) } - pub fn bottom_center(&self) -> Point { + pub const fn bottom_center(&self) -> Point { self.bottom_left().center(self.bottom_right()) } - pub fn contains(&self, point: Point) -> bool { + pub const fn contains(&self, point: Point) -> bool { point.x >= self.x0 && point.x < self.x1 && point.y >= self.y0 && point.y < self.y1 } - pub fn union(&self, other: Self) -> Self { + pub const fn union(&self, other: Self) -> Self { Self { - x0: self.x0.min(other.x0), - y0: self.y0.min(other.y0), - x1: self.x1.max(other.x1), - y1: self.y1.max(other.y1), + x0: min(self.x0, other.x0), + y0: min(self.y0, other.y0), + x1: max(self.x1, other.x1), + y1: max(self.y1, other.y1), } } @@ -250,7 +304,7 @@ impl Rect { } } - pub fn cut_from_left(&self, width: i32) -> Self { + pub const fn cut_from_left(&self, width: i32) -> Self { Self { x0: self.x0, y0: self.y0, @@ -259,7 +313,7 @@ impl Rect { } } - pub fn cut_from_right(&self, width: i32) -> Self { + pub const fn cut_from_right(&self, width: i32) -> Self { Self { x0: self.x1 - width, y0: self.y0, @@ -268,8 +322,8 @@ impl Rect { } } - pub fn split_top(self, height: i32) -> (Self, Self) { - let height = height.clamp(0, self.height()); + pub const fn split_top(self, height: i32) -> (Self, Self) { + let height = clamp(height, 0, self.height()); let top = Self { y1: self.y0 + height, @@ -282,12 +336,12 @@ impl Rect { (top, bottom) } - pub fn split_bottom(self, height: i32) -> (Self, Self) { + pub const fn split_bottom(self, height: i32) -> (Self, Self) { self.split_top(self.height() - height) } - pub fn split_left(self, width: i32) -> (Self, Self) { - let width = width.clamp(0, self.width()); + pub const fn split_left(self, width: i32) -> (Self, Self) { + let width = clamp(width, 0, self.width()); let left = Self { x1: self.x0 + width, @@ -300,32 +354,16 @@ impl Rect { (left, right) } - pub fn split_right(self, width: i32) -> (Self, Self) { + pub const fn split_right(self, width: i32) -> (Self, Self) { self.split_left(self.width() - width) } - const fn _max(a: i32, b: i32) -> i32 { - if a > b { - a - } else { - b - } - } - - const fn _min(a: i32, b: i32) -> i32 { - if a < b { - a - } else { - b - } - } - pub const fn clamp(self, limit: Rect) -> Self { Self { - x0: Rect::_max(self.x0, limit.x0), - y0: Rect::_max(self.y0, limit.y0), - x1: Rect::_min(self.x1, limit.x1), - y1: Rect::_min(self.y1, limit.y1), + x0: max(self.x0, limit.x0), + y0: max(self.y0, limit.y0), + x1: min(self.x1, limit.x1), + y1: min(self.y1, limit.y1), } } @@ -403,7 +441,7 @@ impl Axis { } } - pub fn cross(self) -> Self { + pub const fn cross(self) -> Self { match self { Axis::Horizontal => Axis::Vertical, Axis::Vertical => Axis::Horizontal, @@ -423,7 +461,7 @@ pub struct Grid { } impl Grid { - pub fn new(area: Rect, rows: usize, cols: usize) -> Self { + pub const fn new(area: Rect, rows: usize, cols: usize) -> Self { Self { rows, cols, @@ -432,16 +470,15 @@ impl Grid { } } - pub fn with_spacing(mut self, spacing: i32) -> Self { - self.spacing = spacing; - self + pub const fn with_spacing(self, spacing: i32) -> Self { + Self { spacing, ..self } } - pub fn row_col(&self, row: usize, col: usize) -> Rect { + pub const fn row_col(&self, row: usize, col: usize) -> Rect { let ncols = self.cols as i32; let nrows = self.rows as i32; - let col = (col as i32).min(ncols - 1); - let row = (row as i32).min(nrows - 1); + let col = min(col as i32, ncols - 1); + let row = min(row as i32, nrows - 1); // Total number of horizontal pixels used for spacing. let spacing_width = self.spacing * (ncols - 1); @@ -457,14 +494,13 @@ impl Grid { let leftover_width = (self.area.width() - spacing_width) % ncols; let leftover_height = (self.area.height() - spacing_height) % nrows; - let mut top_left = self.area.top_left() - + Offset::new( - col * (cell_width + self.spacing), - row * (cell_height + self.spacing), - ); + let mut top_left = self.area.top_left().ofs(Offset::new( + col * (cell_width + self.spacing), + row * (cell_height + self.spacing), + )); // Some previous cells were 1px wider. - top_left.x += leftover_width.min(col); - top_left.y += leftover_height.min(row); + top_left.x += min(leftover_width, col); + top_left.y += min(leftover_height, row); let mut size = Offset::new(cell_width, cell_height); // This cell might be 1px wider. @@ -478,11 +514,11 @@ impl Grid { Rect::from_top_left_and_size(top_left, size) } - pub fn cell(&self, index: usize) -> Rect { + pub const fn cell(&self, index: usize) -> Rect { self.row_col(index / self.cols, index % self.cols) } - pub fn cells(&self, cells: GridCellSpan) -> Rect { + pub const fn cells(&self, cells: GridCellSpan) -> Rect { let from = self.row_col(cells.from.0, cells.from.1); let to = self.row_col(cells.to.0, cells.to.1); from.union(to) @@ -503,7 +539,7 @@ pub struct LinearPlacement { } impl LinearPlacement { - pub fn horizontal() -> Self { + pub const fn horizontal() -> Self { Self { axis: Axis::Horizontal, align: Alignment::Start, @@ -511,7 +547,7 @@ impl LinearPlacement { } } - pub fn vertical() -> Self { + pub const fn vertical() -> Self { Self { axis: Axis::Vertical, align: Alignment::Start, @@ -519,24 +555,29 @@ impl LinearPlacement { } } - pub fn align_at_start(mut self) -> Self { - self.align = Alignment::Start; - self + pub const fn align_at_start(self) -> Self { + Self { + align: Alignment::Start, + ..self + } } - pub fn align_at_center(mut self) -> Self { - self.align = Alignment::Center; - self + pub const fn align_at_center(self) -> Self { + Self { + align: Alignment::Center, + ..self + } } - pub fn align_at_end(mut self) -> Self { - self.align = Alignment::End; - self + pub const fn align_at_end(self) -> Self { + Self { + align: Alignment::End, + ..self + } } - pub fn with_spacing(mut self, spacing: i32) -> Self { - self.spacing = spacing; - self + pub const fn with_spacing(self, spacing: i32) -> Self { + Self { spacing, ..self } } /// Arranges all `items` by parameters configured in `self` into `area`. @@ -582,7 +623,7 @@ impl LinearPlacement { } } - fn compute_spacing(&self, area: Rect, count: usize, size_sum: i32) -> (i32, i32) { + const fn compute_spacing(&self, area: Rect, count: usize, size_sum: i32) -> (i32, i32) { let spacing_count = count.saturating_sub(1); let spacing_sum = spacing_count as i32 * self.spacing; let naive_size = size_sum + spacing_sum; @@ -590,9 +631,9 @@ impl LinearPlacement { // scale down spacing to fit everything into area let (total_size, spacing) = if naive_size > available_space { - let scaled_space = (available_space - size_sum) / spacing_count.max(1) as i32; + let scaled_space = (available_space - size_sum) / max(spacing_count as i32, 1); // forbid negative spacing - (available_space, scaled_space.max(0)) + (available_space, max(scaled_space, 0)) } else { (naive_size, self.spacing) };