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 base_blocks: HashMap<usize, BlockLayout>,
47 pending_paint_spans: HashMap<usize, Vec<PaintSpan>>,
51}
52
53impl Default for FlowLayout {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl FlowLayout {
60 pub fn new() -> Self {
61 Self {
62 blocks: HashMap::new(),
63 tables: HashMap::new(),
64 frames: HashMap::new(),
65 flow_order: Vec::new(),
66 content_height: 0.0,
67 viewport_width: 0.0,
68 viewport_height: 0.0,
69 cached_max_content_width: 0.0,
70 scale_factor: 1.0,
71 base_blocks: HashMap::new(),
72 pending_paint_spans: HashMap::new(),
73 }
74 }
75
76 pub fn add_table(
78 &mut self,
79 registry: &FontRegistry,
80 params: &TableLayoutParams,
81 available_width: f32,
82 ) {
83 let mut table = layout_table(registry, params, available_width, self.scale_factor);
84
85 let mut y = self.content_height;
86 table.y = y;
87 y += table.total_height;
88
89 self.flow_order.push(FlowItem::Table {
90 table_id: table.table_id,
91 y: table.y,
92 height: table.total_height,
93 });
94 if table.total_width > self.cached_max_content_width {
95 self.cached_max_content_width = table.total_width;
96 }
97 self.tables.insert(table.table_id, table);
98 self.content_height = y;
99 }
100
101 pub fn add_frame(
110 &mut self,
111 registry: &FontRegistry,
112 params: &FrameLayoutParams,
113 available_width: f32,
114 ) {
115 use crate::layout::frame::FramePosition;
116
117 let mut frame = layout_frame(registry, params, available_width, self.scale_factor);
118
119 match params.position {
120 FramePosition::Inline => {
121 frame.y = self.content_height;
122 frame.x = 0.0;
123 self.content_height += frame.total_height;
124 }
125 FramePosition::FloatLeft => {
126 frame.y = self.content_height;
127 frame.x = 0.0;
128 self.content_height += frame.total_height;
133 }
134 FramePosition::FloatRight => {
135 frame.y = self.content_height;
136 frame.x = (available_width - frame.total_width).max(0.0);
137 self.content_height += frame.total_height;
138 }
139 FramePosition::Absolute => {
140 frame.y = params.margin_top;
143 frame.x = params.margin_left;
144 }
146 }
147
148 self.flow_order.push(FlowItem::Frame {
149 frame_id: frame.frame_id,
150 y: frame.y,
151 height: frame.total_height,
152 });
153 if frame.total_width > self.cached_max_content_width {
154 self.cached_max_content_width = frame.total_width;
155 }
156 self.frames.insert(frame.frame_id, frame);
157 }
158
159 pub fn clear(&mut self) {
161 self.blocks.clear();
162 self.tables.clear();
163 self.frames.clear();
164 self.flow_order.clear();
165 self.content_height = 0.0;
166 self.cached_max_content_width = 0.0;
167 self.base_blocks.clear();
168 self.pending_paint_spans.clear();
169 }
170
171 pub(crate) fn refresh_base_blocks(&mut self) {
174 self.base_blocks.clear();
175 let mut collected: Vec<(usize, BlockLayout)> = Vec::new();
176 for b in self.blocks.values() {
177 collected.push((b.block_id, b.clone()));
178 }
179 for t in self.tables.values() {
180 collect_table_base(t, &mut collected);
181 }
182 for f in self.frames.values() {
183 collect_frame_base(f, &mut collected);
184 }
185 for (id, b) in collected {
186 self.base_blocks.insert(id, b);
187 }
188 }
189
190 pub fn apply_paint_spans_for(&mut self, spans_by_block: HashMap<usize, Vec<PaintSpan>>) {
198 self.pending_paint_spans = spans_by_block;
199 let base = &self.base_blocks;
200 let pending = &self.pending_paint_spans;
201 for b in self.blocks.values_mut() {
202 overlay_block_in_place(b, base, pending);
203 }
204 for t in self.tables.values_mut() {
205 for c in &mut t.cell_layouts {
206 for b in &mut c.blocks {
207 overlay_block_in_place(b, base, pending);
208 }
209 }
210 }
211 for f in self.frames.values_mut() {
212 overlay_frame_in_place(f, base, pending);
213 }
214 }
215
216 pub fn apply_block_paint_spans(&mut self, block_id: usize, spans: &[PaintSpan]) -> bool {
220 if !self.base_blocks.contains_key(&block_id) {
221 return false;
222 }
223 if spans.is_empty() {
224 self.pending_paint_spans.remove(&block_id);
225 } else {
226 self.pending_paint_spans.insert(block_id, spans.to_vec());
227 }
228 let base = &self.base_blocks;
229 let pending = &self.pending_paint_spans;
230 if let Some(b) = self.blocks.get_mut(&block_id) {
231 overlay_block_in_place(b, base, pending);
232 return true;
233 }
234 for t in self.tables.values_mut() {
235 for c in &mut t.cell_layouts {
236 for b in &mut c.blocks {
237 if b.block_id == block_id {
238 overlay_block_in_place(b, base, pending);
239 return true;
240 }
241 }
242 }
243 }
244 for f in self.frames.values_mut() {
245 if overlay_one_in_frame(f, block_id, base, pending) {
246 return true;
247 }
248 }
249 true
250 }
251
252 fn refresh_base_and_overlay_block(&mut self, block_id: usize) {
264 let fresh = find_block_ref(self, block_id).cloned();
265 if let Some(b) = fresh {
266 self.base_blocks.insert(block_id, b);
267 }
268 if self.pending_paint_spans.is_empty() {
271 return;
272 }
273 let base = &self.base_blocks;
274 let pending = &self.pending_paint_spans;
275 if let Some(b) = self.blocks.get_mut(&block_id) {
276 overlay_block_in_place(b, base, pending);
277 return;
278 }
279 for t in self.tables.values_mut() {
280 for c in &mut t.cell_layouts {
281 for b in &mut c.blocks {
282 if b.block_id == block_id {
283 overlay_block_in_place(b, base, pending);
284 return;
285 }
286 }
287 }
288 }
289 for f in self.frames.values_mut() {
290 if overlay_one_in_frame(f, block_id, base, pending) {
291 return;
292 }
293 }
294 }
295
296 pub fn add_block(
298 &mut self,
299 registry: &FontRegistry,
300 params: &BlockLayoutParams,
301 available_width: f32,
302 ) {
303 let mut block = layout_block(registry, params, available_width, self.scale_factor);
304
305 let mut y = self.content_height;
307 if let Some(FlowItem::Block {
308 block_id: prev_id, ..
309 }) = self.flow_order.last()
310 {
311 if let Some(prev_block) = self.blocks.get(prev_id) {
312 let collapsed = prev_block.bottom_margin.max(block.top_margin);
313 y -= prev_block.bottom_margin;
314 y += collapsed;
315 } else {
316 y += block.top_margin;
317 }
318 } else {
319 y += block.top_margin;
320 }
321
322 block.y = y;
323 let block_content = block.height - block.top_margin - block.bottom_margin;
324 y += block_content + block.bottom_margin;
325
326 self.flow_order.push(FlowItem::Block {
327 block_id: block.block_id,
328 y: block.y,
329 height: block.height,
330 });
331 self.update_max_width_for_block(&block);
332 self.blocks.insert(block.block_id, block);
333 self.content_height = y;
334 }
335
336 pub fn layout_blocks(
338 &mut self,
339 registry: &FontRegistry,
340 block_params: Vec<BlockLayoutParams>,
341 available_width: f32,
342 ) {
343 self.clear();
344 for params in &block_params {
349 self.add_block(registry, params, available_width);
350 }
351 self.refresh_base_blocks();
356 }
357
358 pub fn relayout_block(
363 &mut self,
364 registry: &FontRegistry,
365 params: &BlockLayoutParams,
366 available_width: f32,
367 ) {
368 let block_id = params.block_id;
369
370 if self.blocks.contains_key(&block_id) {
372 self.relayout_top_level_block(registry, params, available_width);
373 self.refresh_base_and_overlay_block(block_id);
374 return;
375 }
376
377 let table_match = self.tables.iter().find_map(|(&tid, table)| {
379 for cell in &table.cell_layouts {
380 if cell.blocks.iter().any(|b| b.block_id == block_id) {
381 return Some((tid, cell.row, cell.column));
382 }
383 }
384 None
385 });
386 if let Some((table_id, row, col)) = table_match {
387 self.relayout_table_block(registry, params, table_id, row, col);
388 self.refresh_base_and_overlay_block(block_id);
389 return;
390 }
391
392 let frame_match = self.frames.iter().find_map(|(&fid, frame)| {
394 if frame_contains_block(frame, block_id) {
395 return Some(fid);
396 }
397 None
398 });
399 if let Some(frame_id) = frame_match {
400 self.relayout_frame_block(registry, params, frame_id);
401 self.refresh_base_and_overlay_block(block_id);
402 }
403 }
404
405 fn relayout_top_level_block(
407 &mut self,
408 registry: &FontRegistry,
409 params: &BlockLayoutParams,
410 available_width: f32,
411 ) {
412 let block_id = params.block_id;
413 let old_y = self.blocks.get(&block_id).map(|b| b.y).unwrap_or(0.0);
414 let old_height = self.blocks.get(&block_id).map(|b| b.height).unwrap_or(0.0);
415 let old_top_margin = self
416 .blocks
417 .get(&block_id)
418 .map(|b| b.top_margin)
419 .unwrap_or(0.0);
420 let old_bottom_margin = self
421 .blocks
422 .get(&block_id)
423 .map(|b| b.bottom_margin)
424 .unwrap_or(0.0);
425 let old_content = old_height - old_top_margin - old_bottom_margin;
426 let old_end = old_y + old_content + old_bottom_margin;
427 let old_char_len = block_char_len(self.blocks.get(&block_id));
428
429 let mut block = layout_block(registry, params, available_width, self.scale_factor);
430 block.y = old_y;
431
432 if (block.top_margin - old_top_margin).abs() > 0.001 {
433 let prev_bm = self.prev_block_bottom_margin(block_id).unwrap_or(0.0);
434 let old_collapsed = prev_bm.max(old_top_margin);
435 let new_collapsed = prev_bm.max(block.top_margin);
436 block.y = old_y + (new_collapsed - old_collapsed);
437 }
438
439 let new_content = block.height - block.top_margin - block.bottom_margin;
440 let new_end = block.y + new_content + block.bottom_margin;
441 let delta = new_end - old_end;
442 let new_char_len = block_char_len(Some(&block));
443 let char_delta = new_char_len as isize - old_char_len as isize;
444
445 let new_y = block.y;
446 let new_height = block.height;
447 self.update_max_width_for_block(&block);
448 self.blocks.insert(block_id, block);
449
450 for item in &mut self.flow_order {
452 if let FlowItem::Block {
453 block_id: id,
454 y,
455 height,
456 } = item
457 && *id == block_id
458 {
459 *y = new_y;
460 *height = new_height;
461 break;
462 }
463 }
464
465 self.shift_items_after_block(block_id, delta);
466 self.shift_block_positions_after_block(block_id, char_delta);
467 }
468
469 fn relayout_table_block(
472 &mut self,
473 registry: &FontRegistry,
474 params: &BlockLayoutParams,
475 table_id: usize,
476 row: usize,
477 col: usize,
478 ) {
479 let table = match self.tables.get_mut(&table_id) {
480 Some(t) => t,
481 None => return,
482 };
483
484 let cell_width = table
485 .column_content_widths
486 .get(col)
487 .copied()
488 .unwrap_or(200.0);
489 let old_table_height = table.total_height;
490
491 let cell = match table
493 .cell_layouts
494 .iter_mut()
495 .find(|c| c.row == row && c.column == col)
496 {
497 Some(c) => c,
498 None => return,
499 };
500
501 let new_block = layout_block(registry, params, cell_width, self.scale_factor);
502 if let Some(old) = cell
503 .blocks
504 .iter_mut()
505 .find(|b| b.block_id == params.block_id)
506 {
507 *old = new_block;
508 }
509
510 let mut block_y = 0.0f32;
512 for block in &mut cell.blocks {
513 block.y = block_y;
514 block_y += block.height;
515 }
516 let cell_height = block_y;
517
518 if row < table.row_heights.len() {
520 let mut max_h = 0.0f32;
521 for c in &table.cell_layouts {
522 if c.row == row {
523 let h: f32 = c.blocks.iter().map(|b| b.height).sum();
524 max_h = max_h.max(h);
525 }
526 }
527 max_h = max_h.max(cell_height);
529 table.row_heights[row] = max_h;
530 }
531
532 let border = table.border_width;
534 let padding = table.cell_padding;
535 let spacing = if table.row_ys.len() > 1 {
536 if table.row_ys.len() >= 2 && !table.row_heights.is_empty() {
538 let expected = table.row_ys[0] + padding + table.row_heights[0] + padding;
539 (table.row_ys.get(1).copied().unwrap_or(expected) - expected).max(0.0)
540 } else {
541 0.0
542 }
543 } else {
544 0.0
545 };
546 let mut y = border;
547 for (r, &row_h) in table.row_heights.iter().enumerate() {
548 if r < table.row_ys.len() {
549 table.row_ys[r] = y + padding;
550 }
551 y += padding * 2.0 + row_h;
552 if r < table.row_heights.len() - 1 {
553 y += spacing;
554 }
555 }
556 table.total_height = y + border;
557
558 let delta = table.total_height - old_table_height;
559
560 for item in &mut self.flow_order {
562 if let FlowItem::Table {
563 table_id: id,
564 height,
565 ..
566 } = item
567 && *id == table_id
568 {
569 *height = table.total_height;
570 break;
571 }
572 }
573
574 self.shift_items_after_table(table_id, delta);
575 }
576
577 fn relayout_frame_block(
580 &mut self,
581 registry: &FontRegistry,
582 params: &BlockLayoutParams,
583 frame_id: usize,
584 ) {
585 let frame = match self.frames.get_mut(&frame_id) {
586 Some(f) => f,
587 None => return,
588 };
589
590 let old_total_height = frame.total_height;
591 let new_block = layout_block(registry, params, frame.content_width, self.scale_factor);
592
593 relayout_block_in_frame(frame, params.block_id, new_block);
594
595 let delta = frame.total_height - old_total_height;
596
597 for item in &mut self.flow_order {
598 if let FlowItem::Frame {
599 frame_id: id,
600 height,
601 ..
602 } = item
603 && *id == frame_id
604 {
605 *height = frame.total_height;
606 break;
607 }
608 }
609
610 self.shift_items_after_frame(frame_id, delta);
611 }
612
613 fn shift_block_positions_after_block(&mut self, block_id: usize, char_delta: isize) {
622 if char_delta == 0 {
623 return;
624 }
625 let refs: Vec<FlowItemRef> = self
628 .flow_order
629 .iter()
630 .map(|item| match item {
631 FlowItem::Block { block_id, .. } => FlowItemRef::Block(*block_id),
632 FlowItem::Table { table_id, .. } => FlowItemRef::Table(*table_id),
633 FlowItem::Frame { frame_id, .. } => FlowItemRef::Frame(*frame_id),
634 })
635 .collect();
636 let mut found = false;
637 for r in refs {
638 match r {
639 FlowItemRef::Block(id) => {
640 if found && let Some(b) = self.blocks.get_mut(&id) {
641 b.position = apply_char_delta(b.position, char_delta);
642 }
643 if id == block_id {
644 found = true;
645 }
646 }
647 FlowItemRef::Table(id) => {
648 if found && let Some(t) = self.tables.get_mut(&id) {
649 shift_block_positions_in_table(t, char_delta);
650 }
651 }
652 FlowItemRef::Frame(id) => {
653 if found && let Some(f) = self.frames.get_mut(&id) {
654 shift_block_positions_in_frame(f, char_delta);
655 }
656 }
657 }
658 }
659 }
660
661 fn shift_items_after_block(&mut self, block_id: usize, delta: f32) {
663 if delta.abs() <= 0.001 {
664 return;
665 }
666 let mut found = false;
667 for item in &mut self.flow_order {
668 match item {
669 FlowItem::Block {
670 block_id: id, y, ..
671 } => {
672 if found {
673 *y += delta;
674 if let Some(b) = self.blocks.get_mut(id) {
675 b.y += delta;
676 }
677 }
678 if *id == block_id {
679 found = true;
680 }
681 }
682 FlowItem::Table {
683 table_id: id, y, ..
684 } => {
685 if found {
686 *y += delta;
687 if let Some(t) = self.tables.get_mut(id) {
688 t.y += delta;
689 }
690 }
691 }
692 FlowItem::Frame {
693 frame_id: id, y, ..
694 } => {
695 if found {
696 *y += delta;
697 if let Some(f) = self.frames.get_mut(id) {
698 f.y += delta;
699 }
700 }
701 }
702 }
703 }
704 self.content_height += delta;
705 }
706
707 fn shift_items_after_table(&mut self, table_id: usize, delta: f32) {
709 if delta.abs() <= 0.001 {
710 return;
711 }
712 let mut found = false;
713 for item in &mut self.flow_order {
714 match item {
715 FlowItem::Table {
716 table_id: id, y, ..
717 } => {
718 if *id == table_id {
719 found = true;
720 continue;
721 }
722 if found {
723 *y += delta;
724 if let Some(t) = self.tables.get_mut(id) {
725 t.y += delta;
726 }
727 }
728 }
729 FlowItem::Block {
730 block_id: id, y, ..
731 } => {
732 if found {
733 *y += delta;
734 if let Some(b) = self.blocks.get_mut(id) {
735 b.y += delta;
736 }
737 }
738 }
739 FlowItem::Frame {
740 frame_id: id, y, ..
741 } => {
742 if found {
743 *y += delta;
744 if let Some(f) = self.frames.get_mut(id) {
745 f.y += delta;
746 }
747 }
748 }
749 }
750 }
751 self.content_height += delta;
752 }
753
754 fn shift_items_after_frame(&mut self, frame_id: usize, delta: f32) {
756 if delta.abs() <= 0.001 {
757 return;
758 }
759 let mut found = false;
760 for item in &mut self.flow_order {
761 match item {
762 FlowItem::Frame {
763 frame_id: id, y, ..
764 } => {
765 if *id == frame_id {
766 found = true;
767 continue;
768 }
769 if found {
770 *y += delta;
771 if let Some(f) = self.frames.get_mut(id) {
772 f.y += delta;
773 }
774 }
775 }
776 FlowItem::Block {
777 block_id: id, y, ..
778 } => {
779 if found {
780 *y += delta;
781 if let Some(b) = self.blocks.get_mut(id) {
782 b.y += delta;
783 }
784 }
785 }
786 FlowItem::Table {
787 table_id: id, y, ..
788 } => {
789 if found {
790 *y += delta;
791 if let Some(t) = self.tables.get_mut(id) {
792 t.y += delta;
793 }
794 }
795 }
796 }
797 }
798 self.content_height += delta;
799 }
800
801 fn update_max_width_for_block(&mut self, block: &BlockLayout) {
803 for line in &block.lines {
804 let w = line.width + block.left_margin + block.right_margin;
805 if w > self.cached_max_content_width {
806 self.cached_max_content_width = w;
807 }
808 }
809 }
810
811 fn prev_block_bottom_margin(&self, block_id: usize) -> Option<f32> {
813 let mut prev_bm = None;
814 for item in &self.flow_order {
815 match item {
816 FlowItem::Block { block_id: id, .. } => {
817 if *id == block_id {
818 return prev_bm;
819 }
820 if let Some(b) = self.blocks.get(id) {
821 prev_bm = Some(b.bottom_margin);
822 }
823 }
824 _ => {
825 prev_bm = None;
827 }
828 }
829 }
830 None
831 }
832}
833
834enum FlowItemRef {
835 Block(usize),
836 Table(usize),
837 Frame(usize),
838}
839
840fn block_char_len(block: Option<&BlockLayout>) -> usize {
841 block
842 .and_then(|b| b.lines.last().map(|l| l.char_range.end))
843 .unwrap_or(0)
844}
845
846fn apply_char_delta(position: usize, delta: isize) -> usize {
847 if delta >= 0 {
848 position + delta as usize
849 } else {
850 position.saturating_sub((-delta) as usize)
851 }
852}
853
854fn shift_block_positions_in_slice(blocks: &mut [BlockLayout], delta: isize) {
855 for block in blocks {
856 block.position = apply_char_delta(block.position, delta);
857 }
858}
859
860fn shift_block_positions_in_table(table: &mut TableLayout, delta: isize) {
861 for cell in &mut table.cell_layouts {
862 shift_block_positions_in_slice(&mut cell.blocks, delta);
863 }
864}
865
866fn shift_block_positions_in_frame(frame: &mut FrameLayout, delta: isize) {
867 shift_block_positions_in_slice(&mut frame.blocks, delta);
868 for table in &mut frame.tables {
869 shift_block_positions_in_table(table, delta);
870 }
871 for nested in &mut frame.frames {
872 shift_block_positions_in_frame(nested, delta);
873 }
874}
875
876fn overlay_block_in_place(
881 b: &mut BlockLayout,
882 base: &HashMap<usize, BlockLayout>,
883 pending: &HashMap<usize, Vec<PaintSpan>>,
884) {
885 if let Some(base_b) = base.get(&b.block_id) {
886 let empty: Vec<PaintSpan> = Vec::new();
887 let spans = pending.get(&b.block_id).unwrap_or(&empty);
888 *b = apply_paint_spans(base_b, spans);
889 }
890}
891
892fn overlay_frame_in_place(
894 frame: &mut FrameLayout,
895 base: &HashMap<usize, BlockLayout>,
896 pending: &HashMap<usize, Vec<PaintSpan>>,
897) {
898 for b in &mut frame.blocks {
899 overlay_block_in_place(b, base, pending);
900 }
901 for t in &mut frame.tables {
902 for c in &mut t.cell_layouts {
903 for b in &mut c.blocks {
904 overlay_block_in_place(b, base, pending);
905 }
906 }
907 }
908 for nested in &mut frame.frames {
909 overlay_frame_in_place(nested, base, pending);
910 }
911}
912
913fn overlay_one_in_frame(
916 frame: &mut FrameLayout,
917 block_id: usize,
918 base: &HashMap<usize, BlockLayout>,
919 pending: &HashMap<usize, Vec<PaintSpan>>,
920) -> bool {
921 for b in &mut frame.blocks {
922 if b.block_id == block_id {
923 overlay_block_in_place(b, base, pending);
924 return true;
925 }
926 }
927 for t in &mut frame.tables {
928 for c in &mut t.cell_layouts {
929 for b in &mut c.blocks {
930 if b.block_id == block_id {
931 overlay_block_in_place(b, base, pending);
932 return true;
933 }
934 }
935 }
936 }
937 for nested in &mut frame.frames {
938 if overlay_one_in_frame(nested, block_id, base, pending) {
939 return true;
940 }
941 }
942 false
943}
944
945fn collect_table_base(t: &TableLayout, out: &mut Vec<(usize, BlockLayout)>) {
946 for c in &t.cell_layouts {
947 for b in &c.blocks {
948 out.push((b.block_id, b.clone()));
949 }
950 }
951}
952
953fn collect_frame_base(f: &FrameLayout, out: &mut Vec<(usize, BlockLayout)>) {
954 for b in &f.blocks {
955 out.push((b.block_id, b.clone()));
956 }
957 for t in &f.tables {
958 collect_table_base(t, out);
959 }
960 for nested in &f.frames {
961 collect_frame_base(nested, out);
962 }
963}
964
965fn find_block_ref(flow: &FlowLayout, block_id: usize) -> Option<&BlockLayout> {
967 if let Some(b) = flow.blocks.get(&block_id) {
968 return Some(b);
969 }
970 for t in flow.tables.values() {
971 for c in &t.cell_layouts {
972 for b in &c.blocks {
973 if b.block_id == block_id {
974 return Some(b);
975 }
976 }
977 }
978 }
979 for f in flow.frames.values() {
980 if let Some(b) = find_block_in_frame(f, block_id) {
981 return Some(b);
982 }
983 }
984 None
985}
986
987fn find_block_in_frame(frame: &FrameLayout, block_id: usize) -> Option<&BlockLayout> {
988 for b in &frame.blocks {
989 if b.block_id == block_id {
990 return Some(b);
991 }
992 }
993 for t in &frame.tables {
994 for c in &t.cell_layouts {
995 for b in &c.blocks {
996 if b.block_id == block_id {
997 return Some(b);
998 }
999 }
1000 }
1001 }
1002 for nested in &frame.frames {
1003 if let Some(b) = find_block_in_frame(nested, block_id) {
1004 return Some(b);
1005 }
1006 }
1007 None
1008}
1009
1010pub(crate) fn frame_contains_block(frame: &FrameLayout, block_id: usize) -> bool {
1012 if frame.blocks.iter().any(|b| b.block_id == block_id) {
1013 return true;
1014 }
1015 frame
1016 .frames
1017 .iter()
1018 .any(|nested| frame_contains_block(nested, block_id))
1019}
1020
1021fn relayout_block_in_frame(frame: &mut FrameLayout, block_id: usize, new_block: BlockLayout) {
1024 let old_content_height = frame.content_height;
1025
1026 if let Some(old) = frame.blocks.iter_mut().find(|b| b.block_id == block_id) {
1028 *old = new_block;
1029 } else {
1030 for nested in &mut frame.frames {
1032 if frame_contains_block(nested, block_id) {
1033 relayout_block_in_frame(nested, block_id, new_block);
1034 break;
1035 }
1036 }
1037 }
1038
1039 let mut content_y = 0.0f32;
1041 for block in &mut frame.blocks {
1042 block.y = content_y + block.top_margin;
1043 let block_content = block.height - block.top_margin - block.bottom_margin;
1044 content_y = block.y + block_content + block.bottom_margin;
1045 }
1046 for table in &mut frame.tables {
1047 table.y = content_y;
1048 content_y += table.total_height;
1049 }
1050 for nested in &mut frame.frames {
1051 nested.y = content_y;
1052 content_y += nested.total_height;
1053 }
1054
1055 frame.content_height = content_y;
1056 frame.total_height += content_y - old_content_height;
1057}