use crate::font::registry::FontRegistry;
use crate::layout::block::{BlockLayout, BlockLayoutParams, layout_block};
use crate::types::{DecorationKind, DecorationRect};
pub struct TableLayoutParams {
pub table_id: usize,
pub rows: usize,
pub columns: usize,
pub column_widths: Vec<f32>,
pub border_width: f32,
pub cell_spacing: f32,
pub cell_padding: f32,
pub cells: Vec<CellLayoutParams>,
}
pub struct CellLayoutParams {
pub row: usize,
pub column: usize,
pub blocks: Vec<BlockLayoutParams>,
pub background_color: Option<[f32; 4]>,
}
pub struct TableLayout {
pub table_id: usize,
pub y: f32,
pub total_height: f32,
pub total_width: f32,
pub column_xs: Vec<f32>,
pub column_content_widths: Vec<f32>,
pub row_ys: Vec<f32>,
pub row_heights: Vec<f32>,
pub cell_layouts: Vec<CellLayout>,
pub border_width: f32,
pub cell_padding: f32,
}
pub struct CellLayout {
pub row: usize,
pub column: usize,
pub blocks: Vec<BlockLayout>,
pub background_color: Option<[f32; 4]>,
}
pub fn layout_table(
registry: &FontRegistry,
params: &TableLayoutParams,
available_width: f32,
scale_factor: f32,
) -> TableLayout {
let cols = params.columns.max(1);
let rows = params.rows.max(1);
let border = params.border_width;
let padding = params.cell_padding;
let spacing = params.cell_spacing;
let total_overhead =
border * 2.0 + spacing * (cols as f32 - 1.0).max(0.0) + padding * 2.0 * cols as f32;
let content_area = (available_width - total_overhead).max(0.0);
let column_content_widths =
compute_column_widths(¶ms.column_widths, cols, content_area, padding);
let mut column_xs = Vec::with_capacity(cols);
let mut x = border;
for (c, &col_w) in column_content_widths.iter().enumerate() {
column_xs.push(x + padding);
x += padding * 2.0 + col_w;
if c < cols - 1 {
x += spacing;
}
}
let total_width = x + border;
let mut cell_layouts = Vec::new();
let mut row_heights = vec![0.0f32; rows];
for cell_params in ¶ms.cells {
let r = cell_params.row;
let c = cell_params.column;
if c >= cols || r >= rows {
continue;
}
let cell_width = column_content_widths[c];
let mut cell_blocks = Vec::new();
let mut cell_height = 0.0f32;
for block_params in &cell_params.blocks {
let block = layout_block(registry, block_params, cell_width, scale_factor);
cell_height += block.height;
cell_blocks.push(block);
}
let mut block_y = 0.0f32;
for block in &mut cell_blocks {
block.y = block_y;
block_y += block.height;
}
row_heights[r] = row_heights[r].max(cell_height);
cell_layouts.push(CellLayout {
row: r,
column: c,
blocks: cell_blocks,
background_color: cell_params.background_color,
});
}
let mut row_ys = Vec::with_capacity(rows);
let mut y = border;
for (r, &row_h) in row_heights.iter().enumerate() {
row_ys.push(y + padding);
y += padding * 2.0 + row_h;
if r < rows - 1 {
y += spacing;
}
}
let total_height = y + border;
TableLayout {
table_id: params.table_id,
y: 0.0, total_height,
total_width,
column_xs,
column_content_widths,
row_ys,
row_heights,
cell_layouts,
border_width: border,
cell_padding: padding,
}
}
fn compute_column_widths(
specified: &[f32],
cols: usize,
content_area: f32,
_padding: f32,
) -> Vec<f32> {
let default_col_width = if content_area.is_finite() {
(content_area / cols as f32).max(1.0)
} else {
200.0
};
if specified.is_empty() || specified.len() != cols {
return vec![default_col_width; cols];
}
let total: f32 = specified.iter().sum();
if total <= 0.0 {
return vec![default_col_width; cols];
}
specified
.iter()
.map(|&s| (s / total) * content_area)
.collect()
}
pub fn generate_table_decorations(table: &TableLayout, scroll_offset: f32) -> Vec<DecorationRect> {
let mut decorations = Vec::new();
let table_y = table.y - scroll_offset;
if table.border_width > 0.0 {
let bw = table.border_width;
let color = [0.6, 0.6, 0.6, 1.0]; decorations.push(DecorationRect {
rect: [0.0, table_y, table.total_width, bw],
color,
kind: DecorationKind::TableBorder,
});
decorations.push(DecorationRect {
rect: [
0.0,
table_y + table.total_height - bw,
table.total_width,
bw,
],
color,
kind: DecorationKind::TableBorder,
});
decorations.push(DecorationRect {
rect: [0.0, table_y, bw, table.total_height],
color,
kind: DecorationKind::TableBorder,
});
decorations.push(DecorationRect {
rect: [table.total_width - bw, table_y, bw, table.total_height],
color,
kind: DecorationKind::TableBorder,
});
for r in 1..table.row_ys.len() {
let row_y = table.row_ys[r] - table.cell_padding;
decorations.push(DecorationRect {
rect: [0.0, table_y + row_y - bw / 2.0, table.total_width, bw],
color,
kind: DecorationKind::TableBorder,
});
}
for c in 1..table.column_xs.len() {
let col_x = table.column_xs[c] - table.cell_padding;
decorations.push(DecorationRect {
rect: [col_x - bw / 2.0, table_y, bw, table.total_height],
color,
kind: DecorationKind::TableBorder,
});
}
}
for cell in &table.cell_layouts {
if let Some(bg_color) = cell.background_color
&& cell.row < table.row_ys.len()
&& cell.column < table.column_xs.len()
{
let cx = table.column_xs[cell.column] - table.cell_padding;
let cy = table.row_ys[cell.row] - table.cell_padding;
let cw = table.column_content_widths[cell.column] + table.cell_padding * 2.0;
let ch = table.row_heights[cell.row] + table.cell_padding * 2.0;
decorations.push(DecorationRect {
rect: [cx, table_y + cy, cw, ch],
color: bg_color,
kind: DecorationKind::TableCellBackground,
});
}
}
decorations
}