use crate::render::dimension::Pt;
use crate::render::geometry::PtRect;
use crate::render::layout::draw_command::DrawCommand;
use super::borders::{border_width, emit_cell_borders, CellBorders};
use super::grid::is_vmerge_continue;
use super::types::{
CellVAlign, MeasuredRow, MeasuredTable, TableBorderLine, TableRowInput, VerticalMergeState,
};
pub(super) struct TableCommandBuffers<'a> {
pub(super) commands: &'a mut Vec<DrawCommand>,
pub(super) content_commands: &'a mut Vec<DrawCommand>,
pub(super) border_commands: &'a mut Vec<DrawCommand>,
}
pub(super) fn emit_table_rows(
measured: &MeasuredTable,
rows: &[TableRowInput],
row_range: std::ops::Range<usize>,
cursor_y: &mut Pt,
bufs: &mut TableCommandBuffers<'_>,
top_border_override: Option<TableBorderLine>,
) {
let num_rows = measured.rows.len();
let range_start = row_range.start;
for row_idx in row_range {
let mr = &measured.rows[row_idx];
let is_first_in_range = row_idx == range_start;
let has_next_in_slice = row_idx + 1 < num_rows;
emit_one_row(
mr,
&rows[row_idx],
cursor_y,
bufs,
if is_first_in_range {
top_border_override
} else {
None
},
has_next_in_slice,
Some((measured, rows, row_idx)),
);
}
}
pub(super) fn emit_split_row(
mr: &MeasuredRow,
row: &TableRowInput,
cursor_y: &mut Pt,
bufs: &mut TableCommandBuffers<'_>,
top_border_override: Option<TableBorderLine>,
has_next_in_slice: bool,
) {
emit_one_row(
mr,
row,
cursor_y,
bufs,
top_border_override,
has_next_in_slice,
None,
);
}
fn emit_one_row(
mr: &MeasuredRow,
row: &TableRowInput,
cursor_y: &mut Pt,
bufs: &mut TableCommandBuffers<'_>,
top_border_override: Option<TableBorderLine>,
has_next_in_slice: bool,
vmerge_ctx: Option<(&MeasuredTable, &[TableRowInput], usize)>,
) {
let row_height = mr.height;
for (cell_ci, (entry, cell_input)) in mr.entries.iter().zip(row.cells.iter()).enumerate() {
if let Some(color) = cell_input.shading {
bufs.commands.push(DrawCommand::Rect {
rect: PtRect::from_xywh(entry.cell_x, *cursor_y, entry.cell_w, row_height),
color,
});
}
let cell_top = if mr.borders[cell_ci].top.is_none() {
top_border_override
} else {
mr.borders[cell_ci].top
};
let b_left = mr.borders[cell_ci].left;
let b_right = mr.borders[cell_ci].right;
let b_bottom = mr.borders[cell_ci].bottom;
let dx = (border_width(b_left) - cell_input.margins.left).max(Pt::ZERO);
let dy_border = (border_width(cell_top) - cell_input.margins.top).max(Pt::ZERO);
let effective_h = if cell_input.vertical_merge == Some(VerticalMergeState::Restart) {
vmerge_ctx
.map(|(m, rs, row_idx)| merged_span_height(m, rs, row_idx, entry.grid_col))
.unwrap_or(row_height)
} else {
row_height
};
let content_h = entry.layout.content_height + cell_input.margins.vertical();
let dy_valign = match cell_input.vertical_align {
CellVAlign::Bottom => (effective_h - content_h - dy_border).max(Pt::ZERO),
CellVAlign::Center => ((effective_h - content_h - dy_border) * 0.5).max(Pt::ZERO),
CellVAlign::Top => Pt::ZERO,
};
for cmd in &entry.layout.commands {
let mut cmd = cmd.clone();
cmd.shift(entry.cell_x + dx, *cursor_y + dy_border + dy_valign);
bufs.content_commands.push(cmd);
}
let bottom_border_gap = if has_next_in_slice {
border_width(b_bottom)
} else {
Pt::ZERO
};
emit_cell_borders(
bufs.border_commands,
CellBorders {
top: cell_top,
bottom: b_bottom,
left: b_left,
right: b_right,
},
entry.cell_x,
entry.cell_w,
*cursor_y,
row_height + bottom_border_gap,
);
}
*cursor_y += row_height + mr.border_gap_below;
}
fn merged_span_height(
measured: &MeasuredTable,
rows: &[TableRowInput],
start_row: usize,
grid_col: usize,
) -> Pt {
let mut total = measured.rows[start_row].height;
let mut row = start_row + 1;
while row < rows.len() && is_vmerge_continue(&rows[row], grid_col) {
total += measured.rows[row - 1].border_gap_below;
total += measured.rows[row].height;
row += 1;
}
total
}