1use std::collections::HashMap;
2
3use crate::font::registry::FontRegistry;
4use crate::layout::block::{BlockLayout, BlockLayoutParams, layout_block};
5use crate::layout::frame::{FrameLayout, FrameLayoutParams, layout_frame};
6use crate::layout::table::{TableLayout, TableLayoutParams, layout_table};
7
8pub enum FlowItem {
9 Block {
10 block_id: usize,
11 y: f32,
12 height: f32,
13 },
14 Table {
15 table_id: usize,
16 y: f32,
17 height: f32,
18 },
19 Frame {
20 frame_id: usize,
21 y: f32,
22 height: f32,
23 },
24}
25
26pub struct FlowLayout {
27 pub blocks: HashMap<usize, BlockLayout>,
28 pub tables: HashMap<usize, TableLayout>,
29 pub frames: HashMap<usize, FrameLayout>,
30 pub flow_order: Vec<FlowItem>,
31 pub content_height: f32,
32 pub viewport_width: f32,
33 pub viewport_height: f32,
34}
35
36impl Default for FlowLayout {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl FlowLayout {
43 pub fn new() -> Self {
44 Self {
45 blocks: HashMap::new(),
46 tables: HashMap::new(),
47 frames: HashMap::new(),
48 flow_order: Vec::new(),
49 content_height: 0.0,
50 viewport_width: 0.0,
51 viewport_height: 0.0,
52 }
53 }
54
55 pub fn add_table(
57 &mut self,
58 registry: &FontRegistry,
59 params: &TableLayoutParams,
60 available_width: f32,
61 ) {
62 let mut table = layout_table(registry, params, available_width);
63
64 let mut y = self.content_height;
65 table.y = y;
66 y += table.total_height;
67
68 self.flow_order.push(FlowItem::Table {
69 table_id: table.table_id,
70 y: table.y,
71 height: table.total_height,
72 });
73 self.tables.insert(table.table_id, table);
74 self.content_height = y;
75 }
76
77 pub fn add_frame(
86 &mut self,
87 registry: &FontRegistry,
88 params: &FrameLayoutParams,
89 available_width: f32,
90 ) {
91 use crate::layout::frame::FramePosition;
92
93 let mut frame = layout_frame(registry, params, available_width);
94
95 match params.position {
96 FramePosition::Inline => {
97 frame.y = self.content_height;
98 frame.x = 0.0;
99 self.content_height += frame.total_height;
100 }
101 FramePosition::FloatLeft => {
102 frame.y = self.content_height;
103 frame.x = 0.0;
104 self.content_height += frame.total_height;
109 }
110 FramePosition::FloatRight => {
111 frame.y = self.content_height;
112 frame.x = (available_width - frame.total_width).max(0.0);
113 self.content_height += frame.total_height;
114 }
115 FramePosition::Absolute => {
116 frame.y = params.margin_top;
119 frame.x = params.margin_left;
120 }
122 }
123
124 self.flow_order.push(FlowItem::Frame {
125 frame_id: frame.frame_id,
126 y: frame.y,
127 height: frame.total_height,
128 });
129 self.frames.insert(frame.frame_id, frame);
130 }
131
132 pub fn clear(&mut self) {
134 self.blocks.clear();
135 self.tables.clear();
136 self.frames.clear();
137 self.flow_order.clear();
138 self.content_height = 0.0;
139 }
140
141 pub fn add_block(
143 &mut self,
144 registry: &FontRegistry,
145 params: &BlockLayoutParams,
146 available_width: f32,
147 ) {
148 let mut block = layout_block(registry, params, available_width);
149
150 let mut y = self.content_height;
152 if let Some(FlowItem::Block {
153 block_id: prev_id, ..
154 }) = self.flow_order.last()
155 {
156 if let Some(prev_block) = self.blocks.get(prev_id) {
157 let collapsed = prev_block.bottom_margin.max(block.top_margin);
158 y -= prev_block.bottom_margin;
159 y += collapsed;
160 } else {
161 y += block.top_margin;
162 }
163 } else {
164 y += block.top_margin;
165 }
166
167 block.y = y;
168 let block_content = block.height - block.top_margin - block.bottom_margin;
169 y += block_content + block.bottom_margin;
170
171 self.flow_order.push(FlowItem::Block {
172 block_id: block.block_id,
173 y: block.y,
174 height: block.height,
175 });
176 self.blocks.insert(block.block_id, block);
177 self.content_height = y;
178 }
179
180 pub fn layout_blocks(
182 &mut self,
183 registry: &FontRegistry,
184 block_params: Vec<BlockLayoutParams>,
185 available_width: f32,
186 ) {
187 self.clear();
188 for params in &block_params {
193 self.add_block(registry, params, available_width);
194 }
195 }
196
197 pub fn relayout_block(
204 &mut self,
205 registry: &FontRegistry,
206 params: &BlockLayoutParams,
207 available_width: f32,
208 ) {
209 let block_id = params.block_id;
210 let old_y = self.blocks.get(&block_id).map(|b| b.y).unwrap_or(0.0);
211 let old_height = self.blocks.get(&block_id).map(|b| b.height).unwrap_or(0.0);
212 let old_top_margin = self
213 .blocks
214 .get(&block_id)
215 .map(|b| b.top_margin)
216 .unwrap_or(0.0);
217 let old_bottom_margin = self
218 .blocks
219 .get(&block_id)
220 .map(|b| b.bottom_margin)
221 .unwrap_or(0.0);
222 let old_content = old_height - old_top_margin - old_bottom_margin;
223 let old_end = old_y + old_content + old_bottom_margin;
224
225 let mut block = layout_block(registry, params, available_width);
226 block.y = old_y;
227
228 if (block.top_margin - old_top_margin).abs() > 0.001 {
230 let prev_bm = self.prev_block_bottom_margin(block_id).unwrap_or(0.0);
231 let old_collapsed = prev_bm.max(old_top_margin);
232 let new_collapsed = prev_bm.max(block.top_margin);
233 block.y = old_y + (new_collapsed - old_collapsed);
234 }
235
236 let new_content = block.height - block.top_margin - block.bottom_margin;
237 let new_end = block.y + new_content + block.bottom_margin;
238 let delta = new_end - old_end;
239
240 let new_y = block.y;
241 let new_height = block.height;
242 self.blocks.insert(block_id, block);
243
244 for item in &mut self.flow_order {
246 if let FlowItem::Block {
247 block_id: id,
248 y,
249 height,
250 } = item
251 && *id == block_id
252 {
253 *y = new_y;
254 *height = new_height;
255 break;
256 }
257 }
258
259 if delta.abs() > 0.001 {
261 let mut found = false;
262 for item in &mut self.flow_order {
263 match item {
264 FlowItem::Block {
265 block_id: id,
266 y,
267 height: _,
268 } => {
269 if found {
270 *y += delta;
271 if let Some(b) = self.blocks.get_mut(id) {
272 b.y += delta;
273 }
274 }
275 if *id == block_id {
276 found = true;
277 }
278 }
279 FlowItem::Table {
280 table_id: id, y, ..
281 } => {
282 if found {
283 *y += delta;
284 if let Some(t) = self.tables.get_mut(id) {
285 t.y += delta;
286 }
287 }
288 }
289 FlowItem::Frame {
290 frame_id: id, y, ..
291 } => {
292 if found {
293 *y += delta;
294 if let Some(f) = self.frames.get_mut(id) {
295 f.y += delta;
296 }
297 }
298 }
299 }
300 }
301 self.content_height += delta;
302 }
303 }
304
305 fn prev_block_bottom_margin(&self, block_id: usize) -> Option<f32> {
307 let mut prev_bm = None;
308 for item in &self.flow_order {
309 match item {
310 FlowItem::Block { block_id: id, .. } => {
311 if *id == block_id {
312 return prev_bm;
313 }
314 if let Some(b) = self.blocks.get(id) {
315 prev_bm = Some(b.bottom_margin);
316 }
317 }
318 _ => {
319 prev_bm = None;
321 }
322 }
323 }
324 None
325 }
326}