use crate::font::registry::FontRegistry;
use crate::layout::block::layout_block;
use crate::layout::block::{BlockLayout, BlockLayoutParams};
use crate::layout::table::{TableLayout, TableLayoutParams, layout_table};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FramePosition {
#[default]
Inline,
FloatLeft,
FloatRight,
Absolute,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum FrameBorderStyle {
#[default]
Full,
LeftOnly,
None,
}
pub struct FrameLayoutParams {
pub frame_id: usize,
pub position: FramePosition,
pub width: Option<f32>,
pub height: Option<f32>,
pub margin_top: f32,
pub margin_bottom: f32,
pub margin_left: f32,
pub margin_right: f32,
pub padding: f32,
pub border_width: f32,
pub border_style: FrameBorderStyle,
pub blocks: Vec<(usize, BlockLayoutParams)>,
pub tables: Vec<(usize, TableLayoutParams)>,
pub frames: Vec<(usize, FrameLayoutParams)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameChildRef {
Block(usize),
Table(usize),
Frame(usize),
}
pub struct FrameLayout {
pub frame_id: usize,
pub y: f32,
pub x: f32,
pub total_width: f32,
pub total_height: f32,
pub content_x: f32,
pub content_y: f32,
pub content_width: f32,
pub content_height: f32,
pub blocks: Vec<BlockLayout>,
pub tables: Vec<TableLayout>,
pub frames: Vec<FrameLayout>,
pub flow_order: Vec<FrameChildRef>,
pub border_width: f32,
pub border_style: FrameBorderStyle,
}
pub fn layout_frame(
registry: &FontRegistry,
params: &FrameLayoutParams,
available_width: f32,
scale_factor: f32,
font_scale: f32,
) -> FrameLayout {
let border = params.border_width;
let pad = params.padding;
let frame_width = params.width.unwrap_or(available_width);
let content_width =
(frame_width - border * 2.0 - pad * 2.0 - params.margin_left - params.margin_right)
.max(0.0);
enum FlowItem<'a> {
Block(&'a BlockLayoutParams),
Table(&'a TableLayoutParams),
Frame(&'a FrameLayoutParams),
}
let mut ordered: Vec<(usize, FlowItem)> =
Vec::with_capacity(params.blocks.len() + params.tables.len() + params.frames.len());
for (idx, b) in ¶ms.blocks {
ordered.push((*idx, FlowItem::Block(b)));
}
for (idx, t) in ¶ms.tables {
ordered.push((*idx, FlowItem::Table(t)));
}
for (idx, f) in ¶ms.frames {
ordered.push((*idx, FlowItem::Frame(f)));
}
ordered.sort_by_key(|(idx, _)| *idx);
let mut blocks: Vec<BlockLayout> = Vec::with_capacity(params.blocks.len());
let mut tables: Vec<TableLayout> = Vec::with_capacity(params.tables.len());
let mut nested_frames: Vec<FrameLayout> = Vec::with_capacity(params.frames.len());
let mut flow_order: Vec<FrameChildRef> = Vec::with_capacity(ordered.len());
let mut content_y = 0.0f32;
for (_flow_idx, item) in &ordered {
match item {
FlowItem::Block(block_params) => {
let mut block = layout_block(
registry,
block_params,
content_width,
scale_factor,
font_scale,
);
block.y = content_y + block.top_margin;
let block_content = block.height - block.top_margin - block.bottom_margin;
content_y = block.y + block_content + block.bottom_margin;
flow_order.push(FrameChildRef::Block(block.block_id));
blocks.push(block);
}
FlowItem::Table(table_params) => {
let mut table = layout_table(
registry,
table_params,
content_width,
scale_factor,
font_scale,
);
table.y = content_y;
content_y += table.total_height;
flow_order.push(FrameChildRef::Table(table.table_id));
tables.push(table);
}
FlowItem::Frame(frame_params) => {
let mut nested = layout_frame(
registry,
frame_params,
content_width,
scale_factor,
font_scale,
);
nested.y = content_y;
nested.x = 0.0;
content_y += nested.total_height;
flow_order.push(FrameChildRef::Frame(nested.frame_id));
nested_frames.push(nested);
}
}
}
let auto_content_height = content_y;
let content_height = params
.height
.map(|h| (h - border * 2.0 - pad * 2.0).max(0.0))
.unwrap_or(auto_content_height);
let total_height =
params.margin_top + border + pad + content_height + pad + border + params.margin_bottom;
let total_width =
params.margin_left + border + pad + content_width + pad + border + params.margin_right;
let content_x = params.margin_left + border + pad;
let content_y_offset = params.margin_top + border + pad;
FrameLayout {
frame_id: params.frame_id,
y: 0.0, x: 0.0,
total_width,
total_height,
content_x,
content_y: content_y_offset,
content_width,
content_height,
blocks,
tables,
frames: nested_frames,
flow_order,
border_width: border,
border_style: params.border_style,
}
}