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.blocks.clear();
188 self.tables.clear();
189 self.frames.clear();
190 self.flow_order.clear();
191
192 let mut y = 0.0f32;
193
194 for params in &block_params {
195 let mut block = layout_block(registry, params, available_width);
196
197 if let Some(FlowItem::Block {
201 block_id: prev_id, ..
202 }) = self.flow_order.last()
203 {
204 if let Some(prev_block) = self.blocks.get(prev_id) {
205 let collapsed = prev_block.bottom_margin.max(block.top_margin);
206 y -= prev_block.bottom_margin;
209 y += collapsed;
210 } else {
211 y += block.top_margin;
212 }
213 } else {
214 y += block.top_margin;
215 }
216
217 block.y = y;
218 let block_height = block.height - block.top_margin - block.bottom_margin;
219 y += block_height + block.bottom_margin;
220
221 self.flow_order.push(FlowItem::Block {
222 block_id: block.block_id,
223 y: block.y,
224 height: block.height,
225 });
226 self.blocks.insert(block.block_id, block);
227 }
228
229 self.content_height = y;
230 }
235
236 pub fn relayout_block(
238 &mut self,
239 registry: &FontRegistry,
240 params: &BlockLayoutParams,
241 available_width: f32,
242 ) {
243 let block_id = params.block_id;
244 let old_height = self.blocks.get(&block_id).map(|b| b.height).unwrap_or(0.0);
245
246 let mut block = layout_block(registry, params, available_width);
247
248 if let Some(old_block) = self.blocks.get(&block_id) {
250 block.y = old_block.y;
251 }
252
253 let new_height = block.height;
254 let delta = new_height - old_height;
255
256 self.blocks.insert(block_id, block);
257
258 for item in &mut self.flow_order {
260 if let FlowItem::Block {
261 block_id: id,
262 height,
263 ..
264 } = item
265 && *id == block_id
266 {
267 *height = new_height;
268 break;
269 }
270 }
271
272 if delta.abs() > 0.001 {
274 let mut found = false;
275 for item in &mut self.flow_order {
276 match item {
277 FlowItem::Block {
278 block_id: id,
279 y,
280 height: _,
281 } => {
282 if found {
283 *y += delta;
284 if let Some(b) = self.blocks.get_mut(id) {
285 b.y += delta;
286 }
287 }
288 if *id == block_id {
289 found = true;
290 }
291 }
292 FlowItem::Table {
293 table_id: id, y, ..
294 } => {
295 if found {
296 *y += delta;
297 if let Some(t) = self.tables.get_mut(id) {
298 t.y += delta;
299 }
300 }
301 }
302 FlowItem::Frame {
303 frame_id: id, y, ..
304 } => {
305 if found {
306 *y += delta;
307 if let Some(f) = self.frames.get_mut(id) {
308 f.y += delta;
309 }
310 }
311 }
312 }
313 }
314 self.content_height += delta;
315 }
316 }
317}