1use crate::font::registry::FontRegistry;
2use crate::layout::block::layout_block;
3use crate::layout::block::{BlockLayout, BlockLayoutParams};
4use crate::layout::table::{TableLayout, TableLayoutParams, layout_table};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum FramePosition {
16 #[default]
18 Inline,
19 FloatLeft,
22 FloatRight,
25 Absolute,
28}
29
30#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
32pub enum FrameBorderStyle {
33 #[default]
35 Full,
36 LeftOnly,
38 None,
40}
41
42pub struct FrameLayoutParams {
44 pub frame_id: usize,
45 pub position: FramePosition,
46 pub width: Option<f32>,
48 pub height: Option<f32>,
50 pub margin_top: f32,
51 pub margin_bottom: f32,
52 pub margin_left: f32,
53 pub margin_right: f32,
54 pub padding: f32,
55 pub border_width: f32,
56 pub border_style: FrameBorderStyle,
57 pub blocks: Vec<(usize, BlockLayoutParams)>,
64 pub tables: Vec<(usize, TableLayoutParams)>,
65 pub frames: Vec<(usize, FrameLayoutParams)>,
66}
67
68pub struct FrameLayout {
70 pub frame_id: usize,
71 pub y: f32,
72 pub x: f32,
73 pub total_width: f32,
74 pub total_height: f32,
75 pub content_x: f32,
76 pub content_y: f32,
77 pub content_width: f32,
78 pub content_height: f32,
79 pub blocks: Vec<BlockLayout>,
80 pub tables: Vec<TableLayout>,
81 pub frames: Vec<FrameLayout>,
82 pub border_width: f32,
83 pub border_style: FrameBorderStyle,
84}
85
86pub fn layout_frame(
88 registry: &FontRegistry,
89 params: &FrameLayoutParams,
90 available_width: f32,
91 scale_factor: f32,
92) -> FrameLayout {
93 let border = params.border_width;
94 let pad = params.padding;
95 let frame_width = params.width.unwrap_or(available_width);
96 let content_width =
97 (frame_width - border * 2.0 - pad * 2.0 - params.margin_left - params.margin_right)
98 .max(0.0);
99
100 enum FlowItem<'a> {
107 Block(&'a BlockLayoutParams),
108 Table(&'a TableLayoutParams),
109 Frame(&'a FrameLayoutParams),
110 }
111 let mut ordered: Vec<(usize, FlowItem)> =
112 Vec::with_capacity(params.blocks.len() + params.tables.len() + params.frames.len());
113 for (idx, b) in ¶ms.blocks {
114 ordered.push((*idx, FlowItem::Block(b)));
115 }
116 for (idx, t) in ¶ms.tables {
117 ordered.push((*idx, FlowItem::Table(t)));
118 }
119 for (idx, f) in ¶ms.frames {
120 ordered.push((*idx, FlowItem::Frame(f)));
121 }
122 ordered.sort_by_key(|(idx, _)| *idx);
123
124 let mut blocks: Vec<BlockLayout> = Vec::with_capacity(params.blocks.len());
125 let mut tables: Vec<TableLayout> = Vec::with_capacity(params.tables.len());
126 let mut nested_frames: Vec<FrameLayout> = Vec::with_capacity(params.frames.len());
127 let mut content_y = 0.0f32;
128
129 for (_flow_idx, item) in &ordered {
130 match item {
131 FlowItem::Block(block_params) => {
132 let mut block = layout_block(registry, block_params, content_width, scale_factor);
133 block.y = content_y + block.top_margin;
134 let block_content = block.height - block.top_margin - block.bottom_margin;
135 content_y = block.y + block_content + block.bottom_margin;
136 blocks.push(block);
137 }
138 FlowItem::Table(table_params) => {
139 let mut table = layout_table(registry, table_params, content_width, scale_factor);
140 table.y = content_y;
141 content_y += table.total_height;
142 tables.push(table);
143 }
144 FlowItem::Frame(frame_params) => {
145 let mut nested = layout_frame(registry, frame_params, content_width, scale_factor);
146 nested.y = content_y;
147 nested.x = 0.0;
148 content_y += nested.total_height;
149 nested_frames.push(nested);
150 }
151 }
152 }
153
154 let auto_content_height = content_y;
155 let content_height = params
156 .height
157 .map(|h| (h - border * 2.0 - pad * 2.0).max(0.0))
158 .unwrap_or(auto_content_height);
159
160 let total_height =
161 params.margin_top + border + pad + content_height + pad + border + params.margin_bottom;
162 let total_width =
163 params.margin_left + border + pad + content_width + pad + border + params.margin_right;
164
165 let content_x = params.margin_left + border + pad;
166 let content_y_offset = params.margin_top + border + pad;
167
168 FrameLayout {
169 frame_id: params.frame_id,
170 y: 0.0, x: 0.0,
172 total_width,
173 total_height,
174 content_x,
175 content_y: content_y_offset,
176 content_width,
177 content_height,
178 blocks,
179 tables,
180 frames: nested_frames,
181 border_width: border,
182 border_style: params.border_style,
183 }
184}