1use std::collections::HashMap;
2
3use crate::font::registry::FontRegistry;
4use crate::layout::block::{
5 BlockLayout, BlockLayoutParams, PaintSpan, apply_paint_spans, layout_block,
6};
7use crate::layout::frame::{FrameLayout, FrameLayoutParams, layout_frame};
8use crate::layout::table::{TableLayout, TableLayoutParams, layout_table};
9
10pub enum FlowItem {
11 Block {
12 block_id: usize,
13 y: f32,
14 height: f32,
15 },
16 Table {
17 table_id: usize,
18 y: f32,
19 height: f32,
20 },
21 Frame {
22 frame_id: usize,
23 y: f32,
24 height: f32,
25 },
26}
27
28pub struct FlowLayout {
29 pub blocks: HashMap<usize, BlockLayout>,
30 pub tables: HashMap<usize, TableLayout>,
31 pub frames: HashMap<usize, FrameLayout>,
32 pub flow_order: Vec<FlowItem>,
33 pub content_height: f32,
34 pub viewport_width: f32,
35 pub viewport_height: f32,
36 pub cached_max_content_width: f32,
37 pub scale_factor: f32,
41 pub font_scale: f32,
46 base_blocks: HashMap<usize, BlockLayout>,
52 pending_paint_spans: HashMap<usize, Vec<PaintSpan>>,
56}
57
58impl Default for FlowLayout {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl FlowLayout {
65 pub fn new() -> Self {
66 Self {
67 blocks: HashMap::new(),
68 tables: HashMap::new(),
69 frames: HashMap::new(),
70 flow_order: Vec::new(),
71 content_height: 0.0,
72 viewport_width: 0.0,
73 viewport_height: 0.0,
74 cached_max_content_width: 0.0,
75 scale_factor: 1.0,
76 font_scale: 1.0,
77 base_blocks: HashMap::new(),
78 pending_paint_spans: HashMap::new(),
79 }
80 }
81
82 pub fn add_table(
84 &mut self,
85 registry: &FontRegistry,
86 params: &TableLayoutParams,
87 available_width: f32,
88 ) {
89 let mut table = layout_table(
90 registry,
91 params,
92 available_width,
93 self.scale_factor,
94 self.font_scale,
95 );
96
97 let mut y = self.content_height;
98 table.y = y;
99 y += table.total_height;
100
101 self.flow_order.push(FlowItem::Table {
102 table_id: table.table_id,
103 y: table.y,
104 height: table.total_height,
105 });
106 if table.total_width > self.cached_max_content_width {
107 self.cached_max_content_width = table.total_width;
108 }
109 self.tables.insert(table.table_id, table);
110 self.content_height = y;
111 }
112
113 pub fn add_frame(
122 &mut self,
123 registry: &FontRegistry,
124 params: &FrameLayoutParams,
125 available_width: f32,
126 ) {
127 use crate::layout::frame::FramePosition;
128
129 let mut frame = layout_frame(
130 registry,
131 params,
132 available_width,
133 self.scale_factor,
134 self.font_scale,
135 );
136
137 match params.position {
138 FramePosition::Inline => {
139 frame.y = self.content_height;
140 frame.x = 0.0;
141 self.content_height += frame.total_height;
142 }
143 FramePosition::FloatLeft => {
144 frame.y = self.content_height;
145 frame.x = 0.0;
146 self.content_height += frame.total_height;
151 }
152 FramePosition::FloatRight => {
153 frame.y = self.content_height;
154 frame.x = (available_width - frame.total_width).max(0.0);
155 self.content_height += frame.total_height;
156 }
157 FramePosition::Absolute => {
158 frame.y = params.margin_top;
161 frame.x = params.margin_left;
162 }
164 }
165
166 self.flow_order.push(FlowItem::Frame {
167 frame_id: frame.frame_id,
168 y: frame.y,
169 height: frame.total_height,
170 });
171 if frame.total_width > self.cached_max_content_width {
172 self.cached_max_content_width = frame.total_width;
173 }
174 self.frames.insert(frame.frame_id, frame);
175 }
176
177 pub fn clear(&mut self) {
179 self.blocks.clear();
180 self.tables.clear();
181 self.frames.clear();
182 self.flow_order.clear();
183 self.content_height = 0.0;
184 self.cached_max_content_width = 0.0;
185 self.base_blocks.clear();
186 self.pending_paint_spans.clear();
187 }
188
189 pub(crate) fn refresh_base_blocks(&mut self) {
192 self.base_blocks.clear();
193 let mut collected: Vec<(usize, BlockLayout)> = Vec::new();
194 for b in self.blocks.values() {
195 collected.push((b.block_id, b.clone()));
196 }
197 for t in self.tables.values() {
198 collect_table_base(t, &mut collected);
199 }
200 for f in self.frames.values() {
201 collect_frame_base(f, &mut collected);
202 }
203 for (id, b) in collected {
204 self.base_blocks.insert(id, b);
205 }
206 }
207
208 pub fn apply_paint_spans_for(&mut self, spans_by_block: HashMap<usize, Vec<PaintSpan>>) {
216 self.pending_paint_spans = spans_by_block;
217 let base = &self.base_blocks;
218 let pending = &self.pending_paint_spans;
219 for b in self.blocks.values_mut() {
220 overlay_block_in_place(b, base, pending);
221 }
222 for t in self.tables.values_mut() {
223 for c in &mut t.cell_layouts {
224 for b in &mut c.blocks {
225 overlay_block_in_place(b, base, pending);
226 }
227 }
228 }
229 for f in self.frames.values_mut() {
230 overlay_frame_in_place(f, base, pending);
231 }
232 }
233
234 pub fn apply_block_paint_spans(&mut self, block_id: usize, spans: &[PaintSpan]) -> bool {
238 if !self.base_blocks.contains_key(&block_id) {
239 return false;
240 }
241 if spans.is_empty() {
242 self.pending_paint_spans.remove(&block_id);
243 } else {
244 self.pending_paint_spans.insert(block_id, spans.to_vec());
245 }
246 let base = &self.base_blocks;
247 let pending = &self.pending_paint_spans;
248 if let Some(b) = self.blocks.get_mut(&block_id) {
249 overlay_block_in_place(b, base, pending);
250 return true;
251 }
252 for t in self.tables.values_mut() {
253 for c in &mut t.cell_layouts {
254 for b in &mut c.blocks {
255 if b.block_id == block_id {
256 overlay_block_in_place(b, base, pending);
257 return true;
258 }
259 }
260 }
261 }
262 for f in self.frames.values_mut() {
263 if overlay_one_in_frame(f, block_id, base, pending) {
264 return true;
265 }
266 }
267 true
268 }
269
270 fn refresh_base_and_overlay_block(&mut self, block_id: usize) {
282 let fresh = find_block_ref(self, block_id).cloned();
283 if let Some(b) = fresh {
284 self.base_blocks.insert(block_id, b);
285 }
286 if self.pending_paint_spans.is_empty() {
289 return;
290 }
291 let base = &self.base_blocks;
292 let pending = &self.pending_paint_spans;
293 if let Some(b) = self.blocks.get_mut(&block_id) {
294 overlay_block_in_place(b, base, pending);
295 return;
296 }
297 for t in self.tables.values_mut() {
298 for c in &mut t.cell_layouts {
299 for b in &mut c.blocks {
300 if b.block_id == block_id {
301 overlay_block_in_place(b, base, pending);
302 return;
303 }
304 }
305 }
306 }
307 for f in self.frames.values_mut() {
308 if overlay_one_in_frame(f, block_id, base, pending) {
309 return;
310 }
311 }
312 }
313
314 pub fn add_block(
316 &mut self,
317 registry: &FontRegistry,
318 params: &BlockLayoutParams,
319 available_width: f32,
320 ) {
321 let mut block = layout_block(
322 registry,
323 params,
324 available_width,
325 self.scale_factor,
326 self.font_scale,
327 );
328
329 let mut y = self.content_height;
331 if let Some(FlowItem::Block {
332 block_id: prev_id, ..
333 }) = self.flow_order.last()
334 {
335 if let Some(prev_block) = self.blocks.get(prev_id) {
336 let collapsed = prev_block.bottom_margin.max(block.top_margin);
337 y -= prev_block.bottom_margin;
338 y += collapsed;
339 } else {
340 y += block.top_margin;
341 }
342 } else {
343 y += block.top_margin;
344 }
345
346 block.y = y;
347 let block_content = block.height - block.top_margin - block.bottom_margin;
348 y += block_content + block.bottom_margin;
349
350 self.flow_order.push(FlowItem::Block {
351 block_id: block.block_id,
352 y: block.y,
353 height: block.height,
354 });
355 self.update_max_width_for_block(&block);
356 self.blocks.insert(block.block_id, block);
357 self.content_height = y;
358 }
359
360 pub fn layout_blocks(
362 &mut self,
363 registry: &FontRegistry,
364 block_params: Vec<BlockLayoutParams>,
365 available_width: f32,
366 ) {
367 self.clear();
368 for params in &block_params {
373 self.add_block(registry, params, available_width);
374 }
375 self.refresh_base_blocks();
380 }
381
382 pub fn relayout_block(
387 &mut self,
388 registry: &FontRegistry,
389 params: &BlockLayoutParams,
390 available_width: f32,
391 ) {
392 let block_id = params.block_id;
393
394 if self.blocks.contains_key(&block_id) {
396 self.relayout_top_level_block(registry, params, available_width);
397 self.refresh_base_and_overlay_block(block_id);
398 return;
399 }
400
401 let table_match = self.tables.iter().find_map(|(&tid, table)| {
403 for cell in &table.cell_layouts {
404 if cell.blocks.iter().any(|b| b.block_id == block_id) {
405 return Some((tid, cell.row, cell.column));
406 }
407 }
408 None
409 });
410 if let Some((table_id, row, col)) = table_match {
411 let old_char_len = block_char_len(find_block_ref(self, block_id));
412 self.relayout_table_block(registry, params, table_id, row, col);
413 let new_char_len = block_char_len(find_block_ref(self, block_id));
414 let char_delta = new_char_len as isize - old_char_len as isize;
415 self.shift_block_positions_after_table_block(table_id, block_id, char_delta);
416 self.refresh_base_and_overlay_block(block_id);
417 return;
418 }
419
420 let frame_match = self.frames.iter().find_map(|(&fid, frame)| {
422 if frame_contains_block(frame, block_id) {
423 return Some(fid);
424 }
425 None
426 });
427 if let Some(frame_id) = frame_match {
428 let old_char_len = block_char_len(find_block_ref(self, block_id));
429 self.relayout_frame_block(registry, params, frame_id);
430 let new_char_len = block_char_len(find_block_ref(self, block_id));
431 let char_delta = new_char_len as isize - old_char_len as isize;
432 self.shift_block_positions_after_frame_block(frame_id, block_id, char_delta);
433 self.refresh_base_and_overlay_block(block_id);
434 }
435 }
436
437 fn relayout_top_level_block(
439 &mut self,
440 registry: &FontRegistry,
441 params: &BlockLayoutParams,
442 available_width: f32,
443 ) {
444 let block_id = params.block_id;
445 let old_y = self.blocks.get(&block_id).map(|b| b.y).unwrap_or(0.0);
446 let old_height = self.blocks.get(&block_id).map(|b| b.height).unwrap_or(0.0);
447 let old_top_margin = self
448 .blocks
449 .get(&block_id)
450 .map(|b| b.top_margin)
451 .unwrap_or(0.0);
452 let old_bottom_margin = self
453 .blocks
454 .get(&block_id)
455 .map(|b| b.bottom_margin)
456 .unwrap_or(0.0);
457 let old_content = old_height - old_top_margin - old_bottom_margin;
458 let old_end = old_y + old_content + old_bottom_margin;
459 let old_char_len = block_char_len(self.blocks.get(&block_id));
460
461 let mut block = layout_block(
462 registry,
463 params,
464 available_width,
465 self.scale_factor,
466 self.font_scale,
467 );
468 block.y = old_y;
469
470 if (block.top_margin - old_top_margin).abs() > 0.001 {
471 let prev_bm = self.prev_block_bottom_margin(block_id).unwrap_or(0.0);
472 let old_collapsed = prev_bm.max(old_top_margin);
473 let new_collapsed = prev_bm.max(block.top_margin);
474 block.y = old_y + (new_collapsed - old_collapsed);
475 }
476
477 let new_content = block.height - block.top_margin - block.bottom_margin;
478 let new_end = block.y + new_content + block.bottom_margin;
479 let delta = new_end - old_end;
480 let new_char_len = block_char_len(Some(&block));
481 let char_delta = new_char_len as isize - old_char_len as isize;
482
483 let new_y = block.y;
484 let new_height = block.height;
485 self.update_max_width_for_block(&block);
486 self.blocks.insert(block_id, block);
487
488 for item in &mut self.flow_order {
490 if let FlowItem::Block {
491 block_id: id,
492 y,
493 height,
494 } = item
495 && *id == block_id
496 {
497 *y = new_y;
498 *height = new_height;
499 break;
500 }
501 }
502
503 self.shift_items_after_block(block_id, delta);
504 self.shift_block_positions_after_block(block_id, char_delta);
505 }
506
507 fn relayout_table_block(
510 &mut self,
511 registry: &FontRegistry,
512 params: &BlockLayoutParams,
513 table_id: usize,
514 row: usize,
515 col: usize,
516 ) {
517 let table = match self.tables.get_mut(&table_id) {
518 Some(t) => t,
519 None => return,
520 };
521
522 let old_table_height = table.total_height;
523 recompute_table_cell(
524 table,
525 registry,
526 params,
527 row,
528 col,
529 self.scale_factor,
530 self.font_scale,
531 );
532 let delta = table.total_height - old_table_height;
533
534 for item in &mut self.flow_order {
536 if let FlowItem::Table {
537 table_id: id,
538 height,
539 ..
540 } = item
541 && *id == table_id
542 {
543 *height = table.total_height;
544 break;
545 }
546 }
547
548 self.shift_items_after_table(table_id, delta);
549 }
550
551 fn relayout_frame_block(
556 &mut self,
557 registry: &FontRegistry,
558 params: &BlockLayoutParams,
559 frame_id: usize,
560 ) {
561 let old_total_height = match self.frames.get(&frame_id) {
562 Some(f) => f.total_height,
563 None => return,
564 };
565
566 {
567 let frame = self.frames.get_mut(&frame_id).unwrap();
568 relayout_block_deep_in_frame(
569 frame,
570 registry,
571 params,
572 self.scale_factor,
573 self.font_scale,
574 );
575 }
576
577 let new_total_height = self.frames[&frame_id].total_height;
578 let delta = new_total_height - old_total_height;
579
580 for item in &mut self.flow_order {
581 if let FlowItem::Frame {
582 frame_id: id,
583 height,
584 ..
585 } = item
586 && *id == frame_id
587 {
588 *height = new_total_height;
589 break;
590 }
591 }
592
593 self.shift_items_after_frame(frame_id, delta);
594 }
595
596 fn shift_block_positions_after_block(&mut self, block_id: usize, char_delta: isize) {
605 self.shift_block_positions_after_flow_item(FlowItemRef::Block(block_id), char_delta);
606 }
607
608 fn shift_block_positions_after_flow_item(&mut self, target: FlowItemRef, char_delta: isize) {
611 if char_delta == 0 {
612 return;
613 }
614 let refs: Vec<FlowItemRef> = self
617 .flow_order
618 .iter()
619 .map(|item| match item {
620 FlowItem::Block { block_id, .. } => FlowItemRef::Block(*block_id),
621 FlowItem::Table { table_id, .. } => FlowItemRef::Table(*table_id),
622 FlowItem::Frame { frame_id, .. } => FlowItemRef::Frame(*frame_id),
623 })
624 .collect();
625 let mut found = false;
626 for r in refs {
627 if found {
628 match r {
629 FlowItemRef::Block(id) => {
630 if let Some(b) = self.blocks.get_mut(&id) {
631 b.position = apply_char_delta(b.position, char_delta);
632 }
633 }
634 FlowItemRef::Table(id) => {
635 if let Some(t) = self.tables.get_mut(&id) {
636 shift_block_positions_in_table(t, char_delta);
637 }
638 }
639 FlowItemRef::Frame(id) => {
640 if let Some(f) = self.frames.get_mut(&id) {
641 shift_block_positions_in_frame(f, char_delta);
642 }
643 }
644 }
645 } else if r == target {
646 found = true;
647 }
648 }
649 }
650
651 fn shift_block_positions_after_table_block(
655 &mut self,
656 table_id: usize,
657 block_id: usize,
658 char_delta: isize,
659 ) {
660 if char_delta == 0 {
661 return;
662 }
663 if let Some(table) = self.tables.get_mut(&table_id) {
664 shift_table_positions_after_block(table, block_id, char_delta);
665 }
666 self.shift_block_positions_after_flow_item(FlowItemRef::Table(table_id), char_delta);
667 }
668
669 fn shift_block_positions_after_frame_block(
673 &mut self,
674 frame_id: usize,
675 block_id: usize,
676 char_delta: isize,
677 ) {
678 if char_delta == 0 {
679 return;
680 }
681 if let Some(frame) = self.frames.get_mut(&frame_id) {
682 shift_frame_positions_after_block(frame, block_id, char_delta);
683 }
684 self.shift_block_positions_after_flow_item(FlowItemRef::Frame(frame_id), char_delta);
685 }
686
687 fn shift_items_after_block(&mut self, block_id: usize, delta: f32) {
689 if delta.abs() <= 0.001 {
690 return;
691 }
692 let mut found = false;
693 for item in &mut self.flow_order {
694 match item {
695 FlowItem::Block {
696 block_id: id, y, ..
697 } => {
698 if found {
699 *y += delta;
700 if let Some(b) = self.blocks.get_mut(id) {
701 b.y += delta;
702 }
703 }
704 if *id == block_id {
705 found = true;
706 }
707 }
708 FlowItem::Table {
709 table_id: id, y, ..
710 } => {
711 if found {
712 *y += delta;
713 if let Some(t) = self.tables.get_mut(id) {
714 t.y += delta;
715 }
716 }
717 }
718 FlowItem::Frame {
719 frame_id: id, y, ..
720 } => {
721 if found {
722 *y += delta;
723 if let Some(f) = self.frames.get_mut(id) {
724 f.y += delta;
725 }
726 }
727 }
728 }
729 }
730 self.content_height += delta;
731 }
732
733 fn shift_items_after_table(&mut self, table_id: usize, delta: f32) {
735 if delta.abs() <= 0.001 {
736 return;
737 }
738 let mut found = false;
739 for item in &mut self.flow_order {
740 match item {
741 FlowItem::Table {
742 table_id: id, y, ..
743 } => {
744 if *id == table_id {
745 found = true;
746 continue;
747 }
748 if found {
749 *y += delta;
750 if let Some(t) = self.tables.get_mut(id) {
751 t.y += delta;
752 }
753 }
754 }
755 FlowItem::Block {
756 block_id: id, y, ..
757 } => {
758 if found {
759 *y += delta;
760 if let Some(b) = self.blocks.get_mut(id) {
761 b.y += delta;
762 }
763 }
764 }
765 FlowItem::Frame {
766 frame_id: id, y, ..
767 } => {
768 if found {
769 *y += delta;
770 if let Some(f) = self.frames.get_mut(id) {
771 f.y += delta;
772 }
773 }
774 }
775 }
776 }
777 self.content_height += delta;
778 }
779
780 fn shift_items_after_frame(&mut self, frame_id: usize, delta: f32) {
782 if delta.abs() <= 0.001 {
783 return;
784 }
785 let mut found = false;
786 for item in &mut self.flow_order {
787 match item {
788 FlowItem::Frame {
789 frame_id: id, y, ..
790 } => {
791 if *id == frame_id {
792 found = true;
793 continue;
794 }
795 if found {
796 *y += delta;
797 if let Some(f) = self.frames.get_mut(id) {
798 f.y += delta;
799 }
800 }
801 }
802 FlowItem::Block {
803 block_id: id, y, ..
804 } => {
805 if found {
806 *y += delta;
807 if let Some(b) = self.blocks.get_mut(id) {
808 b.y += delta;
809 }
810 }
811 }
812 FlowItem::Table {
813 table_id: id, y, ..
814 } => {
815 if found {
816 *y += delta;
817 if let Some(t) = self.tables.get_mut(id) {
818 t.y += delta;
819 }
820 }
821 }
822 }
823 }
824 self.content_height += delta;
825 }
826
827 fn update_max_width_for_block(&mut self, block: &BlockLayout) {
829 for line in &block.lines {
830 let w = line.width + block.left_margin + block.right_margin;
831 if w > self.cached_max_content_width {
832 self.cached_max_content_width = w;
833 }
834 }
835 }
836
837 fn prev_block_bottom_margin(&self, block_id: usize) -> Option<f32> {
839 let mut prev_bm = None;
840 for item in &self.flow_order {
841 match item {
842 FlowItem::Block { block_id: id, .. } => {
843 if *id == block_id {
844 return prev_bm;
845 }
846 if let Some(b) = self.blocks.get(id) {
847 prev_bm = Some(b.bottom_margin);
848 }
849 }
850 _ => {
851 prev_bm = None;
853 }
854 }
855 }
856 None
857 }
858}
859
860#[derive(Clone, Copy, PartialEq, Eq)]
861enum FlowItemRef {
862 Block(usize),
863 Table(usize),
864 Frame(usize),
865}
866
867fn block_char_len(block: Option<&BlockLayout>) -> usize {
868 block
869 .and_then(|b| b.lines.last().map(|l| l.char_range.end))
870 .unwrap_or(0)
871}
872
873fn apply_char_delta(position: usize, delta: isize) -> usize {
874 if delta >= 0 {
875 position + delta as usize
876 } else {
877 position.saturating_sub((-delta) as usize)
878 }
879}
880
881fn shift_block_positions_in_slice(blocks: &mut [BlockLayout], delta: isize) {
882 for block in blocks {
883 block.position = apply_char_delta(block.position, delta);
884 }
885}
886
887fn shift_block_positions_in_table(table: &mut TableLayout, delta: isize) {
888 for cell in &mut table.cell_layouts {
889 shift_block_positions_in_slice(&mut cell.blocks, delta);
890 }
891}
892
893fn shift_block_positions_in_frame(frame: &mut FrameLayout, delta: isize) {
894 shift_block_positions_in_slice(&mut frame.blocks, delta);
895 for table in &mut frame.tables {
896 shift_block_positions_in_table(table, delta);
897 }
898 for nested in &mut frame.frames {
899 shift_block_positions_in_frame(nested, delta);
900 }
901}
902
903fn shift_table_positions_after_block(
907 table: &mut TableLayout,
908 block_id: usize,
909 delta: isize,
910) -> bool {
911 let mut found = false;
912 for cell in &mut table.cell_layouts {
913 for b in &mut cell.blocks {
914 if found {
915 b.position = apply_char_delta(b.position, delta);
916 } else if b.block_id == block_id {
917 found = true;
918 }
919 }
920 }
921 found
922}
923
924fn shift_frame_positions_after_block(
929 frame: &mut FrameLayout,
930 block_id: usize,
931 delta: isize,
932) -> bool {
933 let order = frame.flow_order.clone();
934 let mut found = false;
935 for child in &order {
936 match child {
937 crate::layout::frame::FrameChildRef::Block(bid) => {
938 if found {
939 if let Some(b) = frame.blocks.iter_mut().find(|b| b.block_id == *bid) {
940 b.position = apply_char_delta(b.position, delta);
941 }
942 } else if *bid == block_id {
943 found = true;
944 }
945 }
946 crate::layout::frame::FrameChildRef::Table(tid) => {
947 if let Some(t) = frame.tables.iter_mut().find(|t| t.table_id == *tid) {
948 if found {
949 shift_block_positions_in_table(t, delta);
950 } else {
951 found = shift_table_positions_after_block(t, block_id, delta);
952 }
953 }
954 }
955 crate::layout::frame::FrameChildRef::Frame(fid) => {
956 if let Some(nested) = frame.frames.iter_mut().find(|f| f.frame_id == *fid) {
957 if found {
958 shift_block_positions_in_frame(nested, delta);
959 } else {
960 found = shift_frame_positions_after_block(nested, block_id, delta);
961 }
962 }
963 }
964 }
965 }
966 found
967}
968
969fn overlay_block_in_place(
974 b: &mut BlockLayout,
975 base: &HashMap<usize, BlockLayout>,
976 pending: &HashMap<usize, Vec<PaintSpan>>,
977) {
978 if let Some(base_b) = base.get(&b.block_id) {
979 let empty: Vec<PaintSpan> = Vec::new();
980 let spans = pending.get(&b.block_id).unwrap_or(&empty);
981 *b = apply_paint_spans(base_b, spans);
982 }
983}
984
985fn overlay_frame_in_place(
987 frame: &mut FrameLayout,
988 base: &HashMap<usize, BlockLayout>,
989 pending: &HashMap<usize, Vec<PaintSpan>>,
990) {
991 for b in &mut frame.blocks {
992 overlay_block_in_place(b, base, pending);
993 }
994 for t in &mut frame.tables {
995 for c in &mut t.cell_layouts {
996 for b in &mut c.blocks {
997 overlay_block_in_place(b, base, pending);
998 }
999 }
1000 }
1001 for nested in &mut frame.frames {
1002 overlay_frame_in_place(nested, base, pending);
1003 }
1004}
1005
1006fn overlay_one_in_frame(
1009 frame: &mut FrameLayout,
1010 block_id: usize,
1011 base: &HashMap<usize, BlockLayout>,
1012 pending: &HashMap<usize, Vec<PaintSpan>>,
1013) -> bool {
1014 for b in &mut frame.blocks {
1015 if b.block_id == block_id {
1016 overlay_block_in_place(b, base, pending);
1017 return true;
1018 }
1019 }
1020 for t in &mut frame.tables {
1021 for c in &mut t.cell_layouts {
1022 for b in &mut c.blocks {
1023 if b.block_id == block_id {
1024 overlay_block_in_place(b, base, pending);
1025 return true;
1026 }
1027 }
1028 }
1029 }
1030 for nested in &mut frame.frames {
1031 if overlay_one_in_frame(nested, block_id, base, pending) {
1032 return true;
1033 }
1034 }
1035 false
1036}
1037
1038fn collect_table_base(t: &TableLayout, out: &mut Vec<(usize, BlockLayout)>) {
1039 for c in &t.cell_layouts {
1040 for b in &c.blocks {
1041 out.push((b.block_id, b.clone()));
1042 }
1043 }
1044}
1045
1046fn collect_frame_base(f: &FrameLayout, out: &mut Vec<(usize, BlockLayout)>) {
1047 for b in &f.blocks {
1048 out.push((b.block_id, b.clone()));
1049 }
1050 for t in &f.tables {
1051 collect_table_base(t, out);
1052 }
1053 for nested in &f.frames {
1054 collect_frame_base(nested, out);
1055 }
1056}
1057
1058fn find_block_ref(flow: &FlowLayout, block_id: usize) -> Option<&BlockLayout> {
1060 if let Some(b) = flow.blocks.get(&block_id) {
1061 return Some(b);
1062 }
1063 for t in flow.tables.values() {
1064 for c in &t.cell_layouts {
1065 for b in &c.blocks {
1066 if b.block_id == block_id {
1067 return Some(b);
1068 }
1069 }
1070 }
1071 }
1072 for f in flow.frames.values() {
1073 if let Some(b) = find_block_in_frame(f, block_id) {
1074 return Some(b);
1075 }
1076 }
1077 None
1078}
1079
1080fn find_block_in_frame(frame: &FrameLayout, block_id: usize) -> Option<&BlockLayout> {
1081 for b in &frame.blocks {
1082 if b.block_id == block_id {
1083 return Some(b);
1084 }
1085 }
1086 for t in &frame.tables {
1087 for c in &t.cell_layouts {
1088 for b in &c.blocks {
1089 if b.block_id == block_id {
1090 return Some(b);
1091 }
1092 }
1093 }
1094 }
1095 for nested in &frame.frames {
1096 if let Some(b) = find_block_in_frame(nested, block_id) {
1097 return Some(b);
1098 }
1099 }
1100 None
1101}
1102
1103pub(crate) fn frame_contains_block(frame: &FrameLayout, block_id: usize) -> bool {
1107 if frame.blocks.iter().any(|b| b.block_id == block_id) {
1108 return true;
1109 }
1110 for table in &frame.tables {
1111 for cell in &table.cell_layouts {
1112 if cell.blocks.iter().any(|b| b.block_id == block_id) {
1113 return true;
1114 }
1115 }
1116 }
1117 frame
1118 .frames
1119 .iter()
1120 .any(|nested| frame_contains_block(nested, block_id))
1121}
1122
1123pub(crate) enum FrameBlockLocation {
1126 DirectBlock,
1128 TableCell(usize, usize, usize),
1131 NestedFrame(usize),
1134}
1135
1136pub(crate) fn find_block_location_in_frame(
1138 frame: &FrameLayout,
1139 block_id: usize,
1140) -> Option<FrameBlockLocation> {
1141 if frame.blocks.iter().any(|b| b.block_id == block_id) {
1142 return Some(FrameBlockLocation::DirectBlock);
1143 }
1144 for table in &frame.tables {
1145 for cell in &table.cell_layouts {
1146 if cell.blocks.iter().any(|b| b.block_id == block_id) {
1147 return Some(FrameBlockLocation::TableCell(
1148 table.table_id,
1149 cell.row,
1150 cell.column,
1151 ));
1152 }
1153 }
1154 }
1155 for nested in &frame.frames {
1156 if frame_contains_block(nested, block_id) {
1157 return Some(FrameBlockLocation::NestedFrame(nested.frame_id));
1158 }
1159 }
1160 None
1161}
1162
1163fn relayout_block_in_frame(frame: &mut FrameLayout, block_id: usize, new_block: BlockLayout) {
1168 if let Some(old) = frame.blocks.iter_mut().find(|b| b.block_id == block_id) {
1169 *old = new_block;
1170 }
1171 reposition_frame_children(frame);
1172}
1173
1174fn relayout_block_deep_in_frame(
1180 frame: &mut FrameLayout,
1181 registry: &FontRegistry,
1182 params: &BlockLayoutParams,
1183 scale_factor: f32,
1184 font_scale: f32,
1185) {
1186 match find_block_location_in_frame(frame, params.block_id) {
1187 None => {}
1188 Some(FrameBlockLocation::DirectBlock) => {
1189 let new_block = layout_block(
1190 registry,
1191 params,
1192 frame.content_width,
1193 scale_factor,
1194 font_scale,
1195 );
1196 relayout_block_in_frame(frame, params.block_id, new_block);
1197 }
1198 Some(FrameBlockLocation::TableCell(table_id, row, col)) => {
1199 if let Some(table) = frame.tables.iter_mut().find(|t| t.table_id == table_id) {
1200 recompute_table_cell(table, registry, params, row, col, scale_factor, font_scale);
1201 }
1202 reposition_frame_children(frame);
1203 }
1204 Some(FrameBlockLocation::NestedFrame(nested_frame_id)) => {
1205 if let Some(nested) = frame
1206 .frames
1207 .iter_mut()
1208 .find(|f| f.frame_id == nested_frame_id)
1209 {
1210 relayout_block_deep_in_frame(nested, registry, params, scale_factor, font_scale);
1211 }
1212 reposition_frame_children(frame);
1213 }
1214 }
1215}
1216
1217fn recompute_table_cell(
1223 table: &mut crate::layout::table::TableLayout,
1224 registry: &FontRegistry,
1225 params: &BlockLayoutParams,
1226 row: usize,
1227 col: usize,
1228 scale_factor: f32,
1229 font_scale: f32,
1230) {
1231 let cell_width = table
1232 .column_content_widths
1233 .get(col)
1234 .copied()
1235 .unwrap_or(200.0);
1236
1237 let cell = match table
1239 .cell_layouts
1240 .iter_mut()
1241 .find(|c| c.row == row && c.column == col)
1242 {
1243 Some(c) => c,
1244 None => return,
1245 };
1246
1247 let new_block = layout_block(registry, params, cell_width, scale_factor, font_scale);
1248 if let Some(old) = cell
1249 .blocks
1250 .iter_mut()
1251 .find(|b| b.block_id == params.block_id)
1252 {
1253 *old = new_block;
1254 }
1255
1256 let mut block_y = 0.0f32;
1258 for block in &mut cell.blocks {
1259 block.y = block_y;
1260 block_y += block.height;
1261 }
1262 let cell_height = block_y;
1263
1264 if row < table.row_heights.len() {
1266 let mut max_h = 0.0f32;
1267 for c in &table.cell_layouts {
1268 if c.row == row {
1269 let h: f32 = c.blocks.iter().map(|b| b.height).sum();
1270 max_h = max_h.max(h);
1271 }
1272 }
1273 max_h = max_h.max(cell_height);
1275 table.row_heights[row] = max_h;
1276 }
1277
1278 let border = table.border_width;
1280 let padding = table.cell_padding;
1281 let spacing = if table.row_ys.len() > 1 {
1282 if table.row_ys.len() >= 2 && !table.row_heights.is_empty() {
1284 let expected = table.row_ys[0] + padding + table.row_heights[0] + padding;
1285 (table.row_ys.get(1).copied().unwrap_or(expected) - expected).max(0.0)
1286 } else {
1287 0.0
1288 }
1289 } else {
1290 0.0
1291 };
1292 let mut y = border;
1293 for (r, &row_h) in table.row_heights.iter().enumerate() {
1294 if r < table.row_ys.len() {
1295 table.row_ys[r] = y + padding;
1296 }
1297 y += padding * 2.0 + row_h;
1298 if r < table.row_heights.len() - 1 {
1299 y += spacing;
1300 }
1301 }
1302 table.total_height = y + border;
1303}
1304
1305pub(crate) fn reposition_frame_children(frame: &mut FrameLayout) {
1315 let old_content_height = frame.content_height;
1316 let order = frame.flow_order.clone();
1317 let mut content_y = 0.0f32;
1318
1319 for child in &order {
1320 match child {
1321 crate::layout::frame::FrameChildRef::Block(bid) => {
1322 if let Some(block) = frame.blocks.iter_mut().find(|b| b.block_id == *bid) {
1323 block.y = content_y + block.top_margin;
1324 let block_content = block.height - block.top_margin - block.bottom_margin;
1325 content_y = block.y + block_content + block.bottom_margin;
1326 }
1327 }
1328 crate::layout::frame::FrameChildRef::Table(tid) => {
1329 if let Some(table) = frame.tables.iter_mut().find(|t| t.table_id == *tid) {
1330 table.y = content_y;
1331 content_y += table.total_height;
1332 }
1333 }
1334 crate::layout::frame::FrameChildRef::Frame(fid) => {
1335 if let Some(nested) = frame.frames.iter_mut().find(|f| f.frame_id == *fid) {
1336 nested.y = content_y;
1337 content_y += nested.total_height;
1338 }
1339 }
1340 }
1341 }
1342
1343 frame.content_height = content_y;
1344 frame.total_height += content_y - old_content_height;
1345}