use super::labels::{self, Label};
use super::shapes::Size;
pub const CLASS_PAD_X: f64 = 12.0;
pub const CLASS_PAD_Y: f64 = 6.0;
pub const ER_CELL_PAD_X: f64 = 8.0;
pub const ER_ROW_PAD_Y: f64 = 4.0;
pub const ER_NAME_PAD_Y: f64 = 6.0;
pub const ER_NAME_PAD_X: f64 = 12.0;
pub const ER_MIN_WIDTH: f64 = 100.0;
#[derive(Debug, Clone, PartialEq)]
pub struct PanelCell {
pub label: Label,
pub x: f64,
pub centered: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct PanelRow {
pub cells: Vec<PanelCell>,
pub y: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Panel {
pub rows: Vec<PanelRow>,
pub rules: Vec<f64>,
pub columns: Vec<f64>,
pub size: Size,
}
impl Panel {
pub fn cell_bounds(&self) -> Vec<(f64, f64, f64, f64)> {
let lh = labels::line_height();
let mut out = Vec::new();
for row in &self.rows {
for cell in &row.cells {
let w = cell.label.width;
let l = if cell.centered {
cell.x - w / 2.0
} else {
cell.x
};
out.push((l, row.y - lh / 2.0, l + w, row.y + lh / 2.0));
}
}
out
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Compartment {
pub lines: Vec<Label>,
pub centered: bool,
}
pub fn class_panel(compartments: &[Compartment]) -> Panel {
let lh = labels::line_height();
let content_width = compartments
.iter()
.flat_map(|c| c.lines.iter())
.map(|l| l.width)
.fold(0.0_f64, f64::max);
let width = content_width + CLASS_PAD_X * 2.0;
let mut rows: Vec<PanelRow> = Vec::new();
let mut rules: Vec<f64> = Vec::new();
let mut y = 0.0_f64;
for (i, compartment) in compartments.iter().enumerate() {
if i > 0 {
rules.push(y);
}
y += CLASS_PAD_Y;
for line in compartment.lines.iter().flat_map(rows_of) {
rows.push(PanelRow {
cells: vec![PanelCell {
label: line,
x: if compartment.centered {
width / 2.0
} else {
CLASS_PAD_X
},
centered: compartment.centered,
}],
y: y + lh / 2.0,
});
y += lh;
}
y += CLASS_PAD_Y;
}
Panel {
rows,
rules,
columns: Vec::new(),
size: Size::new(width, y),
}
}
fn rows_of(label: &Label) -> Vec<Label> {
if label.lines.len() <= 1 {
return vec![label.clone()];
}
label.lines.iter().map(|l| Label::measure(l)).collect()
}
#[derive(Debug, Clone, PartialEq)]
pub struct AttributeRow {
pub kind: Label,
pub name: Label,
pub keys: Label,
pub comment: Label,
}
impl AttributeRow {
fn cells(&self) -> [&Label; 4] {
[&self.kind, &self.name, &self.keys, &self.comment]
}
}
pub fn er_panel(name: &Label, attributes: &[AttributeRow]) -> Panel {
let lh = labels::line_height();
let name_band = lh + ER_NAME_PAD_Y * 2.0;
if attributes.is_empty() {
let width = (name.width + ER_NAME_PAD_X * 2.0).max(ER_MIN_WIDTH);
return Panel {
rows: vec![PanelRow {
cells: vec![PanelCell {
label: name.clone(),
x: width / 2.0,
centered: true,
}],
y: name_band / 2.0,
}],
rules: Vec::new(),
columns: Vec::new(),
size: Size::new(width, name_band),
};
}
let mut present = [true, true, false, false];
let mut widths = [0.0_f64; 4];
for a in attributes {
for (i, cell) in a.cells().into_iter().enumerate() {
if !cell.is_blank() {
present[i] = true;
}
widths[i] = widths[i].max(cell.width);
}
}
for i in 0..4 {
widths[i] = if present[i] {
widths[i] + ER_CELL_PAD_X * 2.0
} else {
0.0
};
}
let columns_width: f64 = widths.iter().sum();
let needed = (name.width + ER_NAME_PAD_X * 2.0).max(ER_MIN_WIDTH);
if needed > columns_width {
let shares = present.iter().filter(|p| **p).count() as f64;
let each = (needed - columns_width) / shares;
for i in 0..4 {
if present[i] {
widths[i] += each;
}
}
}
let width: f64 = widths.iter().sum();
let mut rows = vec![PanelRow {
cells: vec![PanelCell {
label: name.clone(),
x: width / 2.0,
centered: true,
}],
y: name_band / 2.0,
}];
let mut rules = vec![name_band];
let mut columns = Vec::new();
let mut x = 0.0_f64;
for i in 0..3 {
x += widths[i];
if present[i] && widths[i + 1..].iter().any(|w| *w > 0.0) {
columns.push(x);
}
}
let row_height = lh + ER_ROW_PAD_Y * 2.0;
let mut y = name_band;
for (i, a) in attributes.iter().enumerate() {
if i > 0 {
rules.push(y);
}
let mut left = 0.0_f64;
let mut cells = Vec::new();
for (c, cell) in a.cells().into_iter().enumerate() {
if present[c] && !cell.is_blank() {
cells.push(PanelCell {
label: cell.clone(),
x: left + ER_CELL_PAD_X,
centered: false,
});
}
left += widths[c];
}
rows.push(PanelRow {
cells,
y: y + row_height / 2.0,
});
y += row_height;
}
Panel {
rows,
rules,
columns,
size: Size::new(width, y),
}
}