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
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum FrameChildRef {
77 Block(usize),
79 Table(usize),
81 Frame(usize),
83}
84
85pub struct FrameLayout {
87 pub frame_id: usize,
88 pub y: f32,
89 pub x: f32,
90 pub total_width: f32,
91 pub total_height: f32,
92 pub content_x: f32,
93 pub content_y: f32,
94 pub content_width: f32,
95 pub content_height: f32,
96 pub blocks: Vec<BlockLayout>,
97 pub tables: Vec<TableLayout>,
98 pub frames: Vec<FrameLayout>,
99 pub flow_order: Vec<FrameChildRef>,
101 pub border_width: f32,
102 pub border_style: FrameBorderStyle,
103}
104
105pub fn layout_frame(
107 registry: &FontRegistry,
108 params: &FrameLayoutParams,
109 available_width: f32,
110 scale_factor: f32,
111 font_scale: f32,
112) -> FrameLayout {
113 let border = params.border_width;
114 let pad = params.padding;
115 let frame_width = params.width.unwrap_or(available_width);
116 let content_width =
117 (frame_width - border * 2.0 - pad * 2.0 - params.margin_left - params.margin_right)
118 .max(0.0);
119
120 enum FlowItem<'a> {
127 Block(&'a BlockLayoutParams),
128 Table(&'a TableLayoutParams),
129 Frame(&'a FrameLayoutParams),
130 }
131 let mut ordered: Vec<(usize, FlowItem)> =
132 Vec::with_capacity(params.blocks.len() + params.tables.len() + params.frames.len());
133 for (idx, b) in ¶ms.blocks {
134 ordered.push((*idx, FlowItem::Block(b)));
135 }
136 for (idx, t) in ¶ms.tables {
137 ordered.push((*idx, FlowItem::Table(t)));
138 }
139 for (idx, f) in ¶ms.frames {
140 ordered.push((*idx, FlowItem::Frame(f)));
141 }
142 ordered.sort_by_key(|(idx, _)| *idx);
143
144 let mut blocks: Vec<BlockLayout> = Vec::with_capacity(params.blocks.len());
145 let mut tables: Vec<TableLayout> = Vec::with_capacity(params.tables.len());
146 let mut nested_frames: Vec<FrameLayout> = Vec::with_capacity(params.frames.len());
147 let mut flow_order: Vec<FrameChildRef> = Vec::with_capacity(ordered.len());
148 let mut content_y = 0.0f32;
149
150 for (_flow_idx, item) in &ordered {
151 match item {
152 FlowItem::Block(block_params) => {
153 let mut block = layout_block(
154 registry,
155 block_params,
156 content_width,
157 scale_factor,
158 font_scale,
159 );
160 block.y = content_y + block.top_margin;
161 let block_content = block.height - block.top_margin - block.bottom_margin;
162 content_y = block.y + block_content + block.bottom_margin;
163 flow_order.push(FrameChildRef::Block(block.block_id));
164 blocks.push(block);
165 }
166 FlowItem::Table(table_params) => {
167 let mut table = layout_table(
168 registry,
169 table_params,
170 content_width,
171 scale_factor,
172 font_scale,
173 );
174 table.y = content_y;
175 content_y += table.total_height;
176 flow_order.push(FrameChildRef::Table(table.table_id));
177 tables.push(table);
178 }
179 FlowItem::Frame(frame_params) => {
180 let mut nested = layout_frame(
181 registry,
182 frame_params,
183 content_width,
184 scale_factor,
185 font_scale,
186 );
187 nested.y = content_y;
188 nested.x = 0.0;
189 content_y += nested.total_height;
190 flow_order.push(FrameChildRef::Frame(nested.frame_id));
191 nested_frames.push(nested);
192 }
193 }
194 }
195
196 let auto_content_height = content_y;
197 let content_height = params
198 .height
199 .map(|h| (h - border * 2.0 - pad * 2.0).max(0.0))
200 .unwrap_or(auto_content_height);
201
202 let total_height =
203 params.margin_top + border + pad + content_height + pad + border + params.margin_bottom;
204 let total_width =
205 params.margin_left + border + pad + content_width + pad + border + params.margin_right;
206
207 let content_x = params.margin_left + border + pad;
208 let content_y_offset = params.margin_top + border + pad;
209
210 FrameLayout {
211 frame_id: params.frame_id,
212 y: 0.0, x: 0.0,
214 total_width,
215 total_height,
216 content_x,
217 content_y: content_y_offset,
218 content_width,
219 content_height,
220 blocks,
221 tables,
222 frames: nested_frames,
223 flow_order,
224 border_width: border,
225 border_style: params.border_style,
226 }
227}