use crate::geometry::{Rect, Size};
use std::collections::HashMap;
mod length;
mod measure;
mod solver;
#[cfg(test)]
mod tests;
pub use length::Extent;
pub use measure::{Cell, MaxMergeMeasure, Measure, WidthHint};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Axis {
Width,
Height,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Track {
Fixed(Extent),
Fr(f64),
Auto,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CellId(pub u64);
pub enum Node {
#[doc(hidden)]
Grid(GridNode),
#[doc(hidden)]
Cell(Cell),
}
impl From<Grid> for Node {
fn from(g: Grid) -> Self {
Node::Grid(g.node)
}
}
impl From<Cell> for Node {
fn from(c: Cell) -> Self {
Node::Cell(c)
}
}
pub struct Grid {
pub(crate) node: GridNode,
}
#[doc(hidden)]
pub struct GridNode {
pub(crate) cols: Vec<Track>,
pub(crate) rows: Vec<Track>,
pub(crate) gap: (Extent, Extent),
pub(crate) respect: Respect,
pub(crate) id: Option<CellId>,
pub(crate) children: Vec<(Placement, Node)>,
}
#[derive(Clone, Debug, Default)]
pub enum Respect {
#[default]
None,
All,
Matrix(Vec<Vec<bool>>),
}
impl Respect {
pub(crate) fn col_respected(&self, col: usize) -> bool {
match self {
Respect::None => false,
Respect::All => true,
Respect::Matrix(m) => m.iter().any(|row| row.get(col).copied().unwrap_or(false)),
}
}
pub(crate) fn row_respected(&self, row: usize) -> bool {
match self {
Respect::None => false,
Respect::All => true,
Respect::Matrix(m) => m
.get(row)
.map(|cols| cols.iter().any(|b| *b))
.unwrap_or(false),
}
}
}
impl Grid {
pub fn new(
cols: impl IntoIterator<Item = Track>,
rows: impl IntoIterator<Item = Track>,
) -> Self {
Self {
node: GridNode {
cols: cols.into_iter().collect(),
rows: rows.into_iter().collect(),
gap: (Extent::ZERO, Extent::ZERO),
respect: Respect::None,
id: None,
children: Vec::new(),
},
}
}
pub fn cell() -> Cell {
Cell::empty()
}
pub fn id(mut self, id: CellId) -> Self {
self.node.id = Some(id);
self
}
pub fn respect(mut self) -> Self {
self.node.respect = Respect::All;
self
}
pub fn respect_at(mut self, row: usize, col: usize) -> Self {
let nrows = self.node.rows.len();
let ncols = self.node.cols.len();
if row >= nrows || col >= ncols {
return self;
}
let m = match std::mem::replace(&mut self.node.respect, Respect::None) {
Respect::Matrix(mut m) => {
if m.len() < nrows {
m.resize_with(nrows, || vec![false; ncols]);
}
for row_v in m.iter_mut() {
if row_v.len() < ncols {
row_v.resize(ncols, false);
}
}
m
}
_ => vec![vec![false; ncols]; nrows],
};
let mut m = m;
m[row][col] = true;
self.node.respect = Respect::Matrix(m);
self
}
pub fn respect_matrix(mut self, m: Vec<Vec<bool>>) -> Self {
self.node.respect = Respect::Matrix(m);
self
}
pub fn gap(mut self, col: Extent, row: Extent) -> Self {
self.node.gap = (col, row);
self
}
#[must_use]
pub fn place(mut self, placement: Placement, child: impl Into<Node>) -> Self {
self.place_mut(placement, child);
self
}
pub fn place_mut(&mut self, placement: Placement, child: impl Into<Node>) {
self.node.children.push((placement, child.into()));
}
pub fn solve(&self, size: Size, dpi: f64) -> Layout {
solver::solve(&self.node, size, dpi)
}
pub fn try_solve(&self, size: Size, dpi: f64) -> Result<Layout, LayoutError> {
Ok(self.solve(size, dpi))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum LayoutError {}
impl std::fmt::Display for LayoutError {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {}
}
}
impl std::error::Error for LayoutError {}
#[derive(Clone, Debug)]
pub struct Placement {
pub row: u16,
pub col: u16,
pub row_span: u16,
pub col_span: u16,
pub inset: Inset,
}
impl Placement {
pub fn at(row: u16, col: u16) -> Self {
Self {
row,
col,
row_span: 1,
col_span: 1,
inset: Inset::default(),
}
}
pub fn span(mut self, rows: u16, cols: u16) -> Self {
self.row_span = rows.max(1);
self.col_span = cols.max(1);
self
}
pub fn inset(mut self, inset: Inset) -> Self {
self.inset = inset;
self
}
}
#[derive(Clone, Debug, Default)]
pub struct Inset {
pub left: Option<Extent>,
pub right: Option<Extent>,
pub top: Option<Extent>,
pub bottom: Option<Extent>,
pub width: Option<Extent>,
pub height: Option<Extent>,
}
impl Inset {
pub fn all(extent: Extent) -> Self {
Self::default()
.left(extent.clone())
.right(extent.clone())
.top(extent.clone())
.bottom(extent)
}
pub fn left(mut self, l: Extent) -> Self {
self.left = Some(l);
self
}
pub fn right(mut self, l: Extent) -> Self {
self.right = Some(l);
self
}
pub fn top(mut self, l: Extent) -> Self {
self.top = Some(l);
self
}
pub fn bottom(mut self, l: Extent) -> Self {
self.bottom = Some(l);
self
}
pub fn width(mut self, l: Extent) -> Self {
self.width = Some(l);
self
}
pub fn height(mut self, l: Extent) -> Self {
self.height = Some(l);
self
}
}
pub struct Layout {
pub root: Rect,
pub(crate) rects: HashMap<CellId, Rect>,
}
impl Layout {
pub fn rect(&self, id: CellId) -> Option<Rect> {
self.rects.get(&id).copied()
}
pub fn iter(&self) -> impl Iterator<Item = (CellId, Rect)> + '_ {
self.rects.iter().map(|(k, v)| (*k, *v))
}
pub fn translate(&mut self, dx: f64, dy: f64) {
self.root = Rect::new(
self.root.x0 + dx,
self.root.y0 + dy,
self.root.x1 + dx,
self.root.y1 + dy,
);
for rect in self.rects.values_mut() {
*rect = Rect::new(rect.x0 + dx, rect.y0 + dy, rect.x1 + dx, rect.y1 + dy);
}
}
}