feat(core/rust): Add experimental LinearLayout

matejcik/one-of
Jan Pochyla 3 years ago committed by matejcik
parent 3dd3d7f87b
commit 8c47b94b79

@ -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<T> Label<T>
where
T: Deref<Target = [u8]>,
{
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 {

@ -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<T>(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);
}

Loading…
Cancel
Save