1use crate::layout::generic_layout::GenericLayout;
2use crate::util::{block_padding, block_padding2};
3use ratatui::layout::{Flex, Rect, Size};
4use ratatui::widgets::{Block, Borders, Padding};
5use std::borrow::Cow;
6use std::cmp::{max, min};
7use std::collections::VecDeque;
8use std::fmt::{Debug, Formatter};
9use std::hash::Hash;
10use std::mem;
11use std::ops::Range;
12
13#[derive(Debug, Default)]
17pub enum FormLabel {
18 #[default]
20 None,
21 Str(&'static str),
27 String(String),
33 Width(u16),
40 Size(u16, u16),
47}
48
49#[derive(Debug, Default)]
53pub enum FormWidget {
54 #[default]
56 None,
57 Width(u16),
63 Size(u16, u16),
69 StretchY(u16, u16),
78 Wide(u16, u16),
85 StretchX(u16, u16),
89 WideStretchX(u16, u16),
93 StretchXY(u16, u16),
102
103 WideStretchXY(u16, u16),
112}
113
114#[derive(Debug)]
228pub struct LayoutForm<W>
229where
230 W: Eq + Hash + Clone + Debug,
231{
232 spacing: u16,
234 line_spacing: u16,
236 column_spacing: u16,
238 page_border: Padding,
240 mirror_border: bool,
242 columns: u16,
244 flex: Flex,
246 widgets: Vec<WidgetDef<W>>,
248 blocks: VecDeque<BlockDef>,
250 page_breaks: Vec<usize>,
252
253 max_label: u16,
255 max_widget: u16,
256
257 max_left_padding: u16,
259 max_right_padding: u16,
260
261 left_padding: u16,
263 right_padding: u16,
265}
266
267struct WidgetDef<W>
268where
269 W: Debug + Clone,
270{
271 id: W,
273 label: FormLabel,
275 label_str: Option<Cow<'static, str>>,
277 widget: FormWidget,
279}
280
281#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
283pub struct BlockTag(usize);
284
285#[derive(Debug)]
286struct BlockDef {
287 id: BlockTag,
288 block: Option<Block<'static>>,
290 padding: Padding,
292 constructing: bool,
294 range: Range<usize>,
296 area: Rect,
298}
299
300#[derive(Debug)]
301struct BlockOut {
302 block: Option<Block<'static>>,
304 area: Rect,
306}
307
308#[derive(Debug, Default, Clone, Copy)]
310struct XPositions {
311 label_left: u16,
313 label_width: u16,
315 widget_left: u16,
317 widget_width: u16,
319 widget_right: u16,
321 container_left: u16,
323 container_right: u16,
325 total_width: u16,
327}
328
329#[derive(Default, Clone, Copy)]
331struct Page {
332 page_border: Padding,
334 full_width: u16,
336 flex: Flex,
338 max_left_padding: u16,
340 max_right_padding: u16,
342 max_label: u16,
344 max_widget: u16,
346
347 #[allow(dead_code)]
349 width: u16,
350 height: u16,
352 top: u16,
354 bottom: u16,
356 columns: u16,
358 column_spacing: u16,
360 spacing: u16,
362 line_spacing: u16,
364
365 page_no: u16,
367 page_start: u16,
369 page_end: u16,
371
372 y: u16,
374
375 top_padding: u16,
377 bottom_padding: u16,
379 bottom_padding_break: u16,
381 effective_line_spacing: u16,
383
384 x_pos: XPositions,
386}
387
388impl<W> Debug for WidgetDef<W>
389where
390 W: Clone + Debug,
391{
392 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
393 write!(
394 f,
395 "WidgetDef {:?}: {:?} {:?} {:?}",
396 self.id,
397 self.label_str
398 .as_ref()
399 .map(|v| v.as_ref())
400 .unwrap_or_default(),
401 self.label,
402 self.widget
403 )?;
404
405 Ok(())
406 }
407}
408
409impl Debug for Page {
410 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
411 writeln!(
412 f,
413 "Page: [{}x{}] +{}+{} _={}]",
414 self.width, self.height, self.top, self.bottom, self.line_spacing
415 )?;
416 writeln!(
417 f,
418 " page {} {}..{}",
419 self.page_no, self.page_start, self.page_end
420 )?;
421 writeln!(
422 f,
423 " y {} padding {}|{}|{}",
424 self.y, self.top_padding, self.bottom_padding, self.bottom_padding_break
425 )?;
426 writeln!(
427 f,
428 " label {}+{}",
429 self.x_pos.label_left, self.x_pos.label_width
430 )?;
431 writeln!(
432 f,
433 " widget {}+{}",
434 self.x_pos.widget_left, self.x_pos.widget_width
435 )?;
436 writeln!(
437 f,
438 " block {}..{}",
439 self.x_pos.container_left, self.x_pos.container_right
440 )?;
441 write!(
442 f, " total {}",
444 self.x_pos.total_width
445 )?;
446 Ok(())
447 }
448}
449
450impl BlockDef {
451 fn as_out(&self) -> BlockOut {
452 BlockOut {
453 block: self.block.clone(),
454 area: self.area,
455 }
456 }
457}
458
459impl FormWidget {
460 #[inline(always)]
461 fn is_stretch_y(&self) -> bool {
462 match self {
463 FormWidget::None => false,
464 FormWidget::Width(_) => false,
465 FormWidget::Size(_, _) => false,
466 FormWidget::Wide(_, _) => false,
467 FormWidget::StretchX(_, _) => false,
468 FormWidget::WideStretchX(_, _) => false,
469 FormWidget::StretchXY(_, _) => true,
470 FormWidget::WideStretchXY(_, _) => true,
471 FormWidget::StretchY(_, _) => true,
472 }
473 }
474}
475
476impl<W> Default for LayoutForm<W>
477where
478 W: Eq + Clone + Hash + Debug,
479{
480 fn default() -> Self {
481 Self {
482 spacing: 1,
483 line_spacing: Default::default(),
484 column_spacing: 1,
485 page_border: Default::default(),
486 mirror_border: Default::default(),
487 columns: 1,
488 flex: Default::default(),
489 widgets: Default::default(),
490 page_breaks: Default::default(),
491 max_label: Default::default(),
492 max_widget: Default::default(),
493 blocks: Default::default(),
494 max_left_padding: Default::default(),
495 max_right_padding: Default::default(),
496 left_padding: Default::default(),
497 right_padding: Default::default(),
498 }
499 }
500}
501
502impl<W> LayoutForm<W>
503where
504 W: Eq + Hash + Clone + Debug,
505{
506 pub fn new() -> Self {
507 Self::default()
508 }
509
510 #[inline]
512 pub fn spacing(mut self, spacing: u16) -> Self {
513 self.spacing = spacing;
514 self
515 }
516
517 #[inline]
519 pub fn line_spacing(mut self, spacing: u16) -> Self {
520 self.line_spacing = spacing;
521 self
522 }
523
524 #[inline]
526 pub fn column_spacing(mut self, spacing: u16) -> Self {
527 self.column_spacing = spacing;
528 self
529 }
530
531 pub fn border(mut self, border: Padding) -> Self {
533 self.page_border = border;
534 self
535 }
536
537 #[inline]
540 pub fn mirror_odd_border(mut self) -> Self {
541 self.mirror_border = true;
542 self
543 }
544
545 pub fn columns(mut self, columns: u8) -> Self {
547 assert_ne!(columns, 0);
548 self.columns = columns as u16;
549 self
550 }
551
552 #[inline]
554 pub fn flex(mut self, flex: Flex) -> Self {
555 self.flex = flex;
556 self
557 }
558
559 pub fn min_label(mut self, width: u16) -> Self {
561 self.max_label = width;
562 self
563 }
564
565 pub fn min_widget(mut self, width: u16) -> Self {
567 self.max_widget = width;
568 self
569 }
570
571 pub fn start(&mut self, block: Option<Block<'static>>) -> BlockTag {
579 let max_idx = self.widgets.len();
580 let padding = block_padding(&block);
581
582 let tag = BlockTag(self.blocks.len());
583 self.blocks.push_back(BlockDef {
584 id: tag,
585 block,
586 padding,
587 constructing: true,
588 range: max_idx..max_idx,
589 area: Rect::default(),
590 });
591
592 self.left_padding += padding.left;
593 self.right_padding += padding.right;
594
595 self.max_left_padding = max(self.max_left_padding, self.left_padding);
596 self.max_right_padding = max(self.max_right_padding, self.right_padding);
597
598 tag
599 }
600
601 pub fn end(&mut self, tag: BlockTag) {
608 let max = self.widgets.len();
609 for cc in self.blocks.iter_mut().rev() {
610 if cc.id == tag && cc.constructing {
611 cc.range.end = max;
612 cc.constructing = false;
613
614 self.left_padding -= cc.padding.left;
616 self.right_padding -= cc.padding.right;
617
618 return;
619 }
620 if cc.constructing {
621 panic!("Unclosed container {:?}", cc.id);
622 }
623 }
624
625 panic!("No open container.");
626 }
627
628 pub fn end_all(&mut self) {
630 let max = self.widgets.len();
631 for cc in self.blocks.iter_mut().rev() {
632 if cc.constructing {
633 cc.range.end = max;
634 cc.constructing = false;
635
636 self.left_padding -= cc.padding.left;
638 self.right_padding -= cc.padding.right;
639 }
640 }
641 }
642
643 pub fn widgets(&mut self, list: impl IntoIterator<Item = (W, FormLabel, FormWidget)>) {
645 for (k, l, w) in list {
646 self.widget(k, l, w);
647 }
648 }
649
650 pub fn widget(&mut self, key: W, label: FormLabel, widget: FormWidget) {
653 let (label, label_str) = match label {
655 FormLabel::Str(s) => {
656 let width = unicode_display_width::width(s) as u16;
657 (FormLabel::Width(width), Some(Cow::Borrowed(s)))
658 }
659 FormLabel::String(s) => {
660 let width = unicode_display_width::width(&s) as u16;
661 (FormLabel::Width(width), Some(Cow::Owned(s)))
662 }
663 FormLabel::Width(w) => (FormLabel::Width(w), None),
664 FormLabel::Size(w, h) => (FormLabel::Size(w, h), None),
665 FormLabel::None => (FormLabel::None, None),
666 };
667
668 let w = match &label {
669 FormLabel::None => 0,
670 FormLabel::Str(_) | FormLabel::String(_) => {
671 unreachable!()
672 }
673 FormLabel::Width(w) => *w,
674 FormLabel::Size(w, _) => *w,
675 };
676 self.max_label = max(self.max_label, w);
677
678 let w = match &widget {
679 FormWidget::None => 0,
680 FormWidget::Width(w) => *w,
681 FormWidget::Size(w, _) => *w,
682 FormWidget::StretchY(w, _) => *w,
683 FormWidget::Wide(w, _) => *w,
684 FormWidget::StretchX(w, _) => *w,
685 FormWidget::WideStretchX(w, _) => *w,
686 FormWidget::StretchXY(w, _) => *w,
687 FormWidget::WideStretchXY(w, _) => *w,
688 };
689 self.max_widget = max(self.max_widget, w);
690
691 self.widgets.push(WidgetDef {
692 id: key,
693 label,
694 label_str,
695 widget,
696 });
697 }
698
699 pub fn page_break(&mut self) {
703 self.page_breaks.push(self.widgets.len() - 1);
704 }
705
706 fn validate_containers(&self) {
707 for cc in self.blocks.iter() {
708 if cc.constructing {
709 panic!("Unclosed container {:?}", cc.id);
710 }
711 }
712 }
713
714 #[inline(always)]
716 pub fn build_endless(self, width: u16) -> GenericLayout<W> {
717 self.validate_containers();
718 build_layout(self, Size::new(width, u16::MAX), true)
719 }
720
721 #[inline(always)]
723 pub fn build_paged(self, page: Size) -> GenericLayout<W> {
724 self.validate_containers();
725 build_layout(self, page, false)
726 }
727}
728
729impl XPositions {
730 fn new(page: &Page, column: u16, mirror: bool) -> XPositions {
731 let border = if mirror {
732 Padding::new(
733 page.page_border.right,
734 page.page_border.left,
735 page.page_border.top,
736 page.page_border.bottom,
737 )
738 } else {
739 page.page_border
740 };
741
742 let layout_width = page
743 .full_width
744 .saturating_sub(border.left)
745 .saturating_sub(border.right);
746 let column_width = (layout_width / page.columns).saturating_sub(page.column_spacing);
747 let right_margin = page.full_width.saturating_sub(border.right);
748
749 let offset;
750 let label_left;
751 let widget_left;
752 let container_left;
753 let container_right;
754 let widget_right;
755
756 match page.flex {
757 Flex::Legacy => {
758 offset = border.left + (column_width + page.column_spacing) * column;
759 label_left = page.max_left_padding;
760 widget_left = label_left + page.max_label + page.spacing;
761 widget_right = column_width.saturating_sub(page.max_right_padding);
762
763 container_left = 0;
764 container_right = column_width;
765 }
766 Flex::Start => {
767 let single_width = page.max_left_padding
768 + page.max_label
769 + page.spacing
770 + page.max_widget
771 + page.max_right_padding
772 + page.column_spacing;
773
774 offset = border.left + single_width * column;
775 label_left = page.max_left_padding;
776 widget_left = label_left + page.max_label + page.spacing;
777 widget_right = widget_left + page.max_widget;
778
779 container_left = 0;
780 container_right = widget_right + page.max_right_padding;
781 }
782 Flex::Center => {
783 let single_width = page.max_left_padding
784 + page.max_label
785 + page.spacing
786 + page.max_widget
787 + page.max_right_padding
788 + page.column_spacing;
789 let rest = layout_width
790 .saturating_sub(single_width * page.columns)
791 .saturating_add(page.column_spacing);
792
793 offset = border.left + rest / 2 + single_width * column;
794 label_left = page.max_left_padding;
795 widget_left = label_left + page.max_label + page.spacing;
796 widget_right = widget_left + page.max_widget;
797
798 container_left = 0;
799 container_right = widget_right + page.max_right_padding;
800 }
801 Flex::End => {
802 let single_width = page.max_left_padding
803 + page.max_label
804 + page.spacing
805 + page.max_widget
806 + page.max_right_padding
807 + page.column_spacing;
808
809 offset = right_margin
810 .saturating_sub(single_width * (page.columns - column))
811 .saturating_add(page.column_spacing);
812 label_left = page.max_left_padding;
813 widget_left = label_left + page.max_label + page.spacing;
814 widget_right = widget_left + page.max_widget;
815
816 container_left = 0;
817 container_right = widget_right + page.max_right_padding;
818 }
819 Flex::SpaceAround => {
820 let single_width = page.max_left_padding
821 + page.max_label
822 + page.spacing
823 + page.max_widget
824 + page.max_right_padding;
825 let rest = layout_width.saturating_sub(single_width * page.columns);
826 let spacing = rest / (page.columns + 1);
827
828 offset = border.left + spacing + (single_width + spacing) * column;
829 label_left = page.max_left_padding;
830 widget_left = label_left + page.max_label + page.spacing;
831 widget_right = widget_left + page.max_widget;
832
833 container_left = 0;
834 container_right = widget_right + page.max_right_padding;
835 }
836 Flex::SpaceBetween => {
837 let single_width = page.max_left_padding
838 + page.max_label
839 + page.max_widget
840 + page.max_right_padding;
841 let rest = layout_width.saturating_sub(single_width * page.columns);
842 let spacing = if page.columns > 1 {
843 rest / (page.columns - 1)
844 } else {
845 0
846 };
847
848 offset = border.left + (single_width + spacing) * column;
849 label_left = page.max_left_padding;
850 widget_left = label_left + page.max_label + page.spacing;
851 widget_right = widget_left + page.max_widget;
852
853 container_left = 0;
854 container_right = widget_right + page.max_right_padding;
855 }
856 }
857
858 XPositions {
859 container_left: offset + container_left,
860 label_left: offset + label_left,
861 label_width: page.max_label,
862 widget_left: offset + widget_left,
863 widget_width: page.max_widget,
864 container_right: offset + container_right,
865 total_width: widget_right - label_left,
866 widget_right: offset + widget_right,
867 }
868 }
869}
870
871impl Page {
872 fn adjusted_widths<W>(layout: &LayoutForm<W>, page_size: Size) -> (u16, u16, u16)
873 where
874 W: Eq + Hash + Clone + Debug,
875 {
876 let layout_width = page_size
877 .width
878 .saturating_sub(layout.page_border.left)
879 .saturating_sub(layout.page_border.right);
880 let column_width = (layout_width / layout.columns).saturating_sub(layout.column_spacing);
881
882 let mut max_label = layout.max_label;
883 let mut max_widget = layout.max_widget;
884 let mut spacing = layout.spacing;
885
886 let nominal =
887 layout.max_left_padding + max_label + spacing + max_widget + layout.max_right_padding;
888
889 if nominal > column_width {
890 let mut reduce = nominal - column_width;
891
892 if spacing > reduce {
893 spacing -= reduce;
894 reduce = 0;
895 } else {
896 reduce -= spacing;
897 spacing = 0;
898 }
899 if max_label > 5 {
900 if max_label - 5 > reduce {
901 max_label -= reduce;
902 reduce = 0;
903 } else {
904 reduce -= max_label - 5;
905 max_label = 5;
906 }
907 }
908 if max_widget > 5 {
909 if max_widget - 5 > reduce {
910 max_widget -= reduce;
911 reduce = 0;
912 } else {
913 reduce -= max_widget - 5;
914 max_widget = 5;
915 }
916 }
917 if max_label > reduce {
918 max_label -= reduce;
919 reduce = 0;
920 } else {
921 reduce -= max_label;
922 max_label = 0;
923 }
924 if max_widget > reduce {
925 max_widget -= reduce;
926 } else {
928 max_widget = 0;
930 }
931 }
932
933 (max_label, spacing, max_widget)
934 }
935
936 fn new<W>(layout: &LayoutForm<W>, page_size: Size) -> Self
937 where
938 W: Eq + Hash + Clone + Debug,
939 {
940 let (max_label, spacing, max_widget) = Self::adjusted_widths(layout, page_size);
941
942 let mut s = Self {
943 page_border: layout.page_border,
944 full_width: page_size.width,
945 flex: layout.flex,
946 max_left_padding: layout.max_left_padding,
947 max_right_padding: layout.max_right_padding,
948 max_label,
949 max_widget,
950 width: page_size.width,
951 height: page_size.height,
952 top: layout.page_border.top,
953 bottom: layout.page_border.bottom,
954 columns: layout.columns,
955 column_spacing: layout.column_spacing,
956 spacing,
957 line_spacing: layout.line_spacing,
958 page_no: 0,
959 page_start: 0,
960 page_end: page_size.height.saturating_sub(layout.page_border.bottom),
961 y: layout.page_border.top,
962 top_padding: 0,
963 bottom_padding: 0,
964 bottom_padding_break: 0,
965 effective_line_spacing: 0,
966 x_pos: Default::default(),
967 };
968 s.x_pos = XPositions::new(&s, 0, false);
969 s
970 }
971}
972
973fn adjust_blocks<W>(layout: &mut LayoutForm<W>, page_height: u16)
975where
976 W: Eq + Hash + Clone + Debug,
977{
978 if page_height == u16::MAX {
979 return;
980 }
981
982 if page_height < 3 {
983 for block_def in layout.blocks.iter_mut() {
984 if let Some(block) = block_def.block.as_mut() {
985 let padding = block_padding2(block);
986 let borders = if padding.left > 0 {
987 Borders::LEFT
988 } else {
989 Borders::NONE
990 } | if padding.right > 0 {
991 Borders::RIGHT
992 } else {
993 Borders::NONE
994 };
995
996 *block = mem::take(block).borders(borders);
997 block_def.padding.top = 0;
998 block_def.padding.bottom = 0;
999 }
1000 }
1001 }
1002}
1003
1004fn build_layout<W>(mut layout: LayoutForm<W>, page_size: Size, endless: bool) -> GenericLayout<W>
1006where
1007 W: Eq + Hash + Clone + Debug,
1008{
1009 let mut gen_layout = GenericLayout::with_capacity(
1010 layout.widgets.len(), layout.blocks.len() * 2,
1012 );
1013 gen_layout.set_page_size(page_size);
1014
1015 adjust_blocks(&mut layout, page_size.height);
1017
1018 let mut stretch = Vec::with_capacity(layout.widgets.len());
1020 let mut blocks_out = Vec::with_capacity(layout.blocks.len());
1021
1022 let mut saved_page;
1023 let mut page = Page::new(&layout, page_size);
1024
1025 for (idx, widget) in layout.widgets.iter_mut().enumerate() {
1026 saved_page = page;
1028
1029 let mut label_area;
1030 let mut widget_area;
1031
1032 (label_area, widget_area) = next_widget(&mut page, &mut layout.blocks, widget, idx, false);
1033 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1034
1035 if !endless && page.y.saturating_add(page.bottom_padding_break) > page.page_end {
1037 page = saved_page;
1039
1040 blocks_out.clear();
1042 page_break_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1043 push_blocks(&mut blocks_out, &mut gen_layout);
1044 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1045 page_break(&mut page);
1046 assert!(stretch.is_empty());
1047
1048 (label_area, widget_area) =
1050 next_widget(&mut page, &mut layout.blocks, widget, idx, true);
1051 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1052 }
1053
1054 if !endless && widget.widget.is_stretch_y() {
1056 stretch.push(gen_layout.widget_len());
1057 }
1058
1059 gen_layout.add(
1061 widget.id.clone(),
1062 widget_area,
1063 widget.label_str.take(),
1064 label_area,
1065 );
1066
1067 push_blocks(&mut blocks_out, &mut gen_layout);
1068
1069 if !endless && layout.page_breaks.contains(&idx) {
1071 assert!(blocks_out.is_empty());
1072 page_break_blocks(&mut page, &mut layout.blocks, idx + 1, &mut blocks_out);
1073 push_blocks(&mut blocks_out, &mut gen_layout);
1074 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1075 page_break(&mut page);
1076 assert!(stretch.is_empty());
1077 }
1078
1079 drop_blocks(&mut layout.blocks, idx);
1080 assert!(blocks_out.is_empty());
1081 }
1082
1083 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1085
1086 gen_layout.set_page_count(((page.page_no + page.columns) / page.columns) as usize);
1087 gen_layout
1088}
1089
1090fn drop_blocks(_block_def: &mut VecDeque<BlockDef>, _idx: usize) {
1093 }
1106
1107fn page_break_blocks(
1108 page: &mut Page,
1109 block_def: &mut VecDeque<BlockDef>,
1110 idx: usize,
1111 blocks_out: &mut Vec<BlockOut>,
1112) {
1113 for block in block_def.iter_mut().rev() {
1116 if idx > block.range.start && idx < block.range.end {
1117 end_block(page, block);
1118 blocks_out.push(block.as_out());
1119
1120 block.range.start = idx;
1122 }
1123 }
1127}
1128
1129fn page_break(page: &mut Page) {
1131 page.page_no += 1;
1133
1134 let column = page.page_no % page.columns;
1135 let mirror = (page.page_no / page.columns) % 2 == 1;
1136
1137 page.page_start = (page.page_no / page.columns).saturating_mul(page.height);
1138 page.page_end = page
1139 .page_start
1140 .saturating_add(page.height.saturating_sub(page.bottom));
1141 page.x_pos = XPositions::new(page, column, mirror);
1142 page.y = page.page_start.saturating_add(page.top);
1143
1144 page.effective_line_spacing = 0;
1145 page.top_padding = 0;
1146 page.bottom_padding = 0;
1147 page.bottom_padding_break = 0;
1148}
1149
1150fn next_widget<W>(
1152 page: &mut Page,
1153 block_def: &mut VecDeque<BlockDef>,
1154 widget: &WidgetDef<W>,
1155 idx: usize,
1156 must_fit: bool,
1157) -> (Rect, Rect)
1158where
1159 W: Eq + Hash + Clone + Debug,
1160{
1161 page.y = page.y.saturating_add(page.effective_line_spacing);
1163
1164 page.effective_line_spacing = page.line_spacing;
1165 page.top_padding = 0;
1166 page.bottom_padding = 0;
1167 page.bottom_padding_break = 0;
1168
1169 for block in block_def.iter_mut() {
1171 if block.range.start == idx {
1172 start_block(page, block);
1173 }
1174 if block.range.start <= idx {
1175 widget_padding(page, idx, block);
1176 }
1177 }
1181
1182 let (label_area, widget_area, advance) = areas_and_advance(page, widget, must_fit);
1184
1185 page.y = page.y.saturating_add(advance);
1186
1187 (label_area, widget_area)
1188}
1189
1190fn start_block(page: &mut Page, block: &mut BlockDef) {
1192 block.area.x = page.x_pos.container_left;
1194 block.area.width = page
1195 .x_pos
1196 .container_right
1197 .saturating_sub(page.x_pos.container_left);
1198 block.area.y = page.y;
1199
1200 page.y = page.y.saturating_add(block.padding.top);
1202 page.top_padding += block.padding.top;
1203 page.x_pos.container_left = page.x_pos.container_left.saturating_add(block.padding.left);
1204 page.x_pos.container_right = page
1205 .x_pos
1206 .container_right
1207 .saturating_sub(block.padding.right);
1208}
1209
1210fn widget_padding(page: &mut Page, idx: usize, block: &mut BlockDef) {
1211 if block.range.end > idx + 1 {
1212 page.bottom_padding_break += block.padding.bottom;
1213 } else if block.range.end == idx + 1 {
1214 page.bottom_padding += block.padding.bottom;
1215 }
1216}
1217
1218fn next_blocks(
1219 page: &mut Page,
1220 block_def: &mut VecDeque<BlockDef>,
1221 idx: usize,
1222 blocks_out: &mut Vec<BlockOut>,
1223) {
1224 for block in block_def.iter_mut().rev() {
1227 if idx + 1 == block.range.end {
1228 end_block(page, block);
1229 blocks_out.push(block.as_out());
1230 }
1231 }
1235}
1236
1237fn push_blocks<W: Eq + Hash + Clone>(
1238 blocks_out: &mut Vec<BlockOut>,
1239 gen_layout: &mut GenericLayout<W>,
1240) {
1241 while let Some(cc) = blocks_out.pop() {
1242 gen_layout.add_block(cc.area, cc.block);
1243 }
1244}
1245
1246fn end_block(page: &mut Page, block: &mut BlockDef) {
1248 page.y = page.y.saturating_add(block.padding.bottom);
1250 page.x_pos.container_left = page.x_pos.container_left.saturating_sub(block.padding.left);
1251 page.x_pos.container_right = page
1252 .x_pos
1253 .container_right
1254 .saturating_add(block.padding.right);
1255
1256 block.area.height = page.y.saturating_sub(block.area.y);
1258}
1259
1260fn areas_and_advance<W: Debug + Clone>(
1263 page: &Page,
1264 widget: &WidgetDef<W>,
1265 must_fit: bool,
1266) -> (Rect, Rect, u16) {
1267 let stacked = matches!(
1272 widget.widget,
1273 FormWidget::Wide(_, _) | FormWidget::WideStretchX(_, _) | FormWidget::WideStretchXY(_, _)
1274 );
1275
1276 let mut label_height = match &widget.label {
1277 FormLabel::None => 0,
1278 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1279 FormLabel::Width(_) => 1,
1280 FormLabel::Size(_, h) => *h,
1281 };
1282
1283 let mut widget_height = match &widget.widget {
1284 FormWidget::None => 0,
1285 FormWidget::Width(_) => 1,
1286 FormWidget::Size(_, h) => *h,
1287 FormWidget::StretchY(_, h) => *h,
1288 FormWidget::Wide(_, h) => *h,
1289 FormWidget::StretchX(_, h) => *h,
1290 FormWidget::WideStretchX(_, h) => *h,
1291 FormWidget::StretchXY(_, h) => *h,
1292 FormWidget::WideStretchXY(_, h) => *h,
1293 };
1294
1295 let stretch_width = page
1296 .x_pos
1297 .widget_right
1298 .saturating_sub(page.x_pos.widget_left);
1299 let total_stretch_width = page
1300 .x_pos
1301 .widget_right
1302 .saturating_sub(page.x_pos.label_left);
1303
1304 let max_height = if !must_fit {
1305 page.height
1306 .saturating_sub(page.top)
1307 .saturating_sub(page.bottom)
1308 .saturating_sub(page.top_padding)
1309 .saturating_sub(page.bottom_padding)
1310 } else {
1311 page.height
1312 .saturating_sub(page.y - page.page_start)
1313 .saturating_sub(page.bottom)
1314 .saturating_sub(page.bottom_padding_break)
1315 };
1316
1317 if stacked {
1318 if label_height + widget_height > max_height {
1319 label_height = max(1, max_height.saturating_sub(widget_height));
1320 }
1321 if label_height + widget_height > max_height {
1322 widget_height = max(1, max_height.saturating_sub(label_height));
1323 }
1324 if label_height + widget_height > max_height {
1325 label_height = 0;
1326 }
1327 if label_height + widget_height > max_height {
1328 widget_height = max_height;
1329 }
1330
1331 let mut label_area = match &widget.label {
1332 FormLabel::None => Rect::new(
1333 page.x_pos.label_left, page.y,
1335 0,
1336 0,
1337 ),
1338 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1339 FormLabel::Width(_) => Rect::new(
1340 page.x_pos.label_left,
1341 page.y,
1342 page.x_pos.label_width,
1343 label_height,
1344 ),
1345 FormLabel::Size(_, _) => Rect::new(
1346 page.x_pos.label_left,
1347 page.y,
1348 page.x_pos.label_width,
1349 label_height,
1350 ),
1351 };
1352 match &widget.widget {
1353 FormWidget::Wide(_, _) => label_area.width = page.x_pos.total_width,
1354 FormWidget::WideStretchX(_, _) => label_area.width = total_stretch_width,
1355 FormWidget::WideStretchXY(_, _) => label_area.width = total_stretch_width,
1356 _ => {}
1357 }
1358
1359 let widget_area = match &widget.widget {
1360 FormWidget::None => unreachable!(),
1361 FormWidget::Width(_) => unreachable!(),
1362 FormWidget::Size(_, _) => unreachable!(),
1363 FormWidget::StretchY(_, _) => unreachable!(),
1364 FormWidget::Wide(_, _) => Rect::new(
1365 page.x_pos.label_left,
1366 page.y + label_height,
1367 page.x_pos.total_width,
1368 widget_height,
1369 ),
1370 FormWidget::StretchX(_, _) => unreachable!(),
1371 FormWidget::WideStretchX(_, _) => Rect::new(
1372 page.x_pos.label_left,
1373 page.y + label_height,
1374 total_stretch_width,
1375 widget_height,
1376 ),
1377 FormWidget::StretchXY(_, _) => unreachable!(),
1378 FormWidget::WideStretchXY(_, _) => Rect::new(
1379 page.x_pos.label_left,
1380 page.y + label_height,
1381 total_stretch_width,
1382 widget_height,
1383 ),
1384 };
1385
1386 (
1387 label_area,
1388 widget_area,
1389 label_area.height + widget_area.height,
1390 )
1391 } else {
1392 label_height = min(label_height, max_height);
1393 widget_height = min(widget_height, max_height);
1394 let height = max(label_height, widget_height);
1395
1396 let label_area = match &widget.label {
1397 FormLabel::None => Rect::new(
1398 page.x_pos.label_left, page.y,
1400 0,
1401 0,
1402 ),
1403 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1404 FormLabel::Width(_) => Rect::new(
1405 page.x_pos.label_left,
1406 page.y,
1407 page.x_pos.label_width,
1408 height,
1409 ),
1410 FormLabel::Size(_, _) => Rect::new(
1411 page.x_pos.label_left,
1412 page.y,
1413 page.x_pos.label_width,
1414 height,
1415 ),
1416 };
1417
1418 let widget_area = match &widget.widget {
1419 FormWidget::None => Rect::default(),
1420 FormWidget::Width(w) => Rect::new(
1421 page.x_pos.widget_left,
1422 page.y,
1423 min(*w, page.x_pos.widget_width),
1424 height,
1425 ),
1426 FormWidget::Size(w, _) => Rect::new(
1427 page.x_pos.widget_left,
1428 page.y,
1429 min(*w, page.x_pos.widget_width),
1430 height,
1431 ),
1432 FormWidget::StretchY(w, _) => Rect::new(
1433 page.x_pos.widget_left,
1434 page.y,
1435 min(*w, page.x_pos.widget_width),
1436 height,
1437 ),
1438 FormWidget::Wide(_, _) => unreachable!(),
1439 FormWidget::StretchX(_, _) => Rect::new(
1440 page.x_pos.widget_left, page.y,
1442 stretch_width,
1443 height,
1444 ),
1445 FormWidget::WideStretchX(_, _) => unreachable!(),
1446 FormWidget::StretchXY(_, _) => Rect::new(
1447 page.x_pos.widget_left, page.y,
1449 stretch_width,
1450 height,
1451 ),
1452 FormWidget::WideStretchXY(_, _) => unreachable!(),
1453 };
1454
1455 (
1456 label_area,
1457 widget_area,
1458 max(label_area.height, widget_area.height),
1459 )
1460 }
1461}
1462
1463fn adjust_y_stretch<W: Eq + Hash + Clone>(
1466 page: &Page,
1467 stretch_y: &mut Vec<usize>,
1468 gen_layout: &mut GenericLayout<W>,
1469) {
1470 let mut remainder = page.page_end.saturating_sub(page.y);
1471 if remainder == 0 {
1472 stretch_y.clear();
1473 return;
1474 }
1475
1476 let mut n = stretch_y.len() as u16;
1477 for y_idx in stretch_y.drain(..) {
1478 let stretch = remainder / n;
1481 remainder -= stretch;
1482 n -= 1;
1483
1484 let mut area = gen_layout.widget(y_idx);
1486 let test_y = area.bottom();
1487 let test_x = page.x_pos.container_left;
1488
1489 area.height += stretch;
1490 gen_layout.set_widget(y_idx, area);
1491
1492 for idx in y_idx + 1..gen_layout.widget_len() {
1494 let mut area = gen_layout.widget(idx);
1495 if area.y >= test_y {
1496 area.y += stretch;
1497 }
1498 gen_layout.set_widget(idx, area);
1499
1500 let mut area = gen_layout.label(idx);
1501 if area.y >= test_y {
1502 area.y += stretch;
1503 }
1504 gen_layout.set_label(idx, area);
1505 }
1506
1507 for idx in 0..gen_layout.block_len() {
1509 let mut area = gen_layout.block_area(idx);
1510 if area.x >= test_x && area.y >= test_y {
1511 area.y += stretch;
1512 }
1513 if area.x >= test_x && area.y <= test_y && area.bottom() > test_y {
1515 area.height += stretch;
1516 }
1517 gen_layout.set_block_area(idx, area);
1518 }
1519 }
1520}