1use crate::layout::generic_layout::GenericLayout;
2use crate::util::{block_padding, block_padding2};
3use ratatui_core::layout::{Flex, Rect, Size};
4use ratatui_widgets::block::{Block, Padding};
5use ratatui_widgets::borders::Borders;
6use std::borrow::Cow;
7use std::cmp::{max, min};
8use std::collections::VecDeque;
9use std::fmt::Debug;
10use std::hash::Hash;
11use std::mem;
12use std::ops::Range;
13use std::rc::Rc;
14
15#[derive(Debug, Default)]
19pub enum FormLabel {
20 #[default]
22 None,
23 Str(&'static str),
30 String(String),
37 Width(u16),
44 Size(u16, u16),
51}
52
53#[derive(Debug, Default)]
57pub enum FormWidget {
58 #[default]
60 None,
61 Width(u16),
67 Size(u16, u16),
73 StretchY(u16, u16),
82 Wide(u16, u16),
89 StretchX(u16, u16),
93 WideStretchX(u16, u16),
97 StretchXY(u16, u16),
106
107 WideStretchXY(u16, u16),
116}
117
118#[derive(Debug)]
233pub struct LayoutForm<W = usize>
234where
235 W: Eq + Hash + Clone + Debug,
236{
237 spacing: u16,
239 line_spacing: u16,
241 column_spacing: u16,
243 page_border: Padding,
245 mirror_border: bool,
247 columns: u16,
249 flex: Flex,
251 widgets: Vec<WidgetDef<W>>,
253 blocks: VecDeque<BlockDef>,
255 page_breaks: Vec<usize>,
257
258 min_label: u16,
260 min_widget: u16,
261 min_widget_wide: u16,
262
263 max_left_padding: u16,
265 max_right_padding: u16,
266
267 left_padding: u16,
269 right_padding: u16,
271}
272
273#[derive(Debug)]
274struct WidgetDef<W>
275where
276 W: Debug + Clone,
277{
278 id: W,
280 label: FormLabel,
282 label_str: Option<Cow<'static, str>>,
284 widget: FormWidget,
286 gap: u16,
288}
289
290#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
292pub struct BlockTag(usize);
293
294#[derive(Debug)]
295struct BlockDef {
296 id: BlockTag,
297 block: Option<Block<'static>>,
299 padding: Padding,
301 constructing: bool,
303 range: Range<usize>,
305 area: Rect,
307}
308
309#[derive(Debug)]
310struct BlockOut {
311 block: Option<Block<'static>>,
313 area: Rect,
315}
316
317#[derive(Debug, Default, Clone, Copy)]
319struct XPositions {
320 label_left: u16,
322 label_width: u16,
324 widget_left: u16,
326 widget_width: u16,
328 widget_right: u16,
330 container_left: u16,
332 container_right: u16,
334 total_width: u16,
336}
337
338#[derive(Default, Debug, Clone)]
340struct PageDef {
341 page_border: Padding,
343 full_width: u16,
345 flex: Flex,
347 max_left_padding: u16,
349 max_right_padding: u16,
351 max_label: u16,
353 max_widget: u16,
355
356 #[allow(dead_code)]
358 width: u16,
359 height: u16,
361 top: u16,
363 bottom: u16,
365 columns: u16,
367 column_spacing: u16,
369 spacing: u16,
371 line_spacing: u16,
373}
374
375#[derive(Default, Debug, Clone)]
377struct Page {
378 def: Rc<PageDef>,
380 page_no: u16,
382 page_start: u16,
384 page_end: u16,
386
387 y: u16,
389
390 top_padding: u16,
392 bottom_padding: u16,
394 bottom_padding_break: u16,
396 effective_line_spacing: u16,
398
399 x_pos: XPositions,
401}
402
403impl BlockDef {
404 fn as_out(&self) -> BlockOut {
405 BlockOut {
406 block: self.block.clone(),
407 area: self.area,
408 }
409 }
410}
411
412impl FormWidget {
413 #[inline(always)]
414 fn is_stretch_y(&self) -> bool {
415 match self {
416 FormWidget::None => false,
417 FormWidget::Width(_) => false,
418 FormWidget::Size(_, _) => false,
419 FormWidget::Wide(_, _) => false,
420 FormWidget::StretchX(_, _) => false,
421 FormWidget::WideStretchX(_, _) => false,
422 FormWidget::StretchXY(_, _) => true,
423 FormWidget::WideStretchXY(_, _) => true,
424 FormWidget::StretchY(_, _) => true,
425 }
426 }
427}
428
429impl<W> Default for LayoutForm<W>
430where
431 W: Eq + Clone + Hash + Debug,
432{
433 fn default() -> Self {
434 Self {
435 spacing: 1,
436 line_spacing: Default::default(),
437 column_spacing: 1,
438 page_border: Default::default(),
439 mirror_border: Default::default(),
440 columns: 1,
441 flex: Default::default(),
442 widgets: Default::default(),
443 page_breaks: Default::default(),
444 min_label: Default::default(),
445 min_widget: Default::default(),
446 min_widget_wide: Default::default(),
447 blocks: Default::default(),
448 max_left_padding: Default::default(),
449 max_right_padding: Default::default(),
450 left_padding: Default::default(),
451 right_padding: Default::default(),
452 }
453 }
454}
455
456impl<W> LayoutForm<W>
457where
458 W: Eq + Hash + Clone + Debug,
459{
460 pub fn new() -> Self {
461 Self::default()
462 }
463
464 #[inline]
466 pub fn spacing(mut self, spacing: u16) -> Self {
467 self.spacing = spacing;
468 self
469 }
470
471 #[inline]
473 pub fn line_spacing(mut self, spacing: u16) -> Self {
474 self.line_spacing = spacing;
475 self
476 }
477
478 #[inline]
480 pub fn column_spacing(mut self, spacing: u16) -> Self {
481 self.column_spacing = spacing;
482 self
483 }
484
485 pub fn padding(mut self, border: Padding) -> Self {
487 self.page_border = border;
488 self
489 }
490
491 #[deprecated(since = "2.0.0", note = "use padding. is clearer.")]
493 pub fn border(mut self, border: Padding) -> Self {
494 self.page_border = border;
495 self
496 }
497
498 #[inline]
501 pub fn mirror_odd_border(mut self) -> Self {
502 self.mirror_border = true;
503 self
504 }
505
506 pub fn columns(mut self, columns: u8) -> Self {
508 assert_ne!(columns, 0);
509 self.columns = columns as u16;
510 self
511 }
512
513 #[inline]
515 pub fn flex(mut self, flex: Flex) -> Self {
516 self.flex = flex;
517 self
518 }
519
520 pub fn min_label(mut self, width: u16) -> Self {
522 self.min_label = width;
523 self
524 }
525
526 pub fn min_widget(mut self, width: u16) -> Self {
528 self.min_widget = width;
529 self
530 }
531
532 pub fn start(&mut self, block: Option<Block<'static>>) -> BlockTag {
540 let max_idx = self.widgets.len();
541 let padding = block_padding(&block);
542
543 let tag = BlockTag(self.blocks.len());
544 self.blocks.push_back(BlockDef {
545 id: tag,
546 block,
547 padding,
548 constructing: true,
549 range: max_idx..max_idx,
550 area: Rect::default(),
551 });
552
553 self.left_padding += padding.left;
554 self.right_padding += padding.right;
555
556 self.max_left_padding = max(self.max_left_padding, self.left_padding);
557 self.max_right_padding = max(self.max_right_padding, self.right_padding);
558
559 tag
560 }
561
562 pub fn end(&mut self, tag: BlockTag) {
569 let max = self.widgets.len();
570 for cc in self.blocks.iter_mut().rev() {
571 if cc.id == tag && cc.constructing {
572 cc.range.end = max;
573 cc.constructing = false;
574
575 self.left_padding -= cc.padding.left;
577 self.right_padding -= cc.padding.right;
578
579 return;
580 }
581 if cc.constructing {
582 panic!("Unclosed container {:?}", cc.id);
583 }
584 }
585
586 panic!("No open container.");
587 }
588
589 pub fn end_all(&mut self) {
591 let max = self.widgets.len();
592 for cc in self.blocks.iter_mut().rev() {
593 if cc.constructing {
594 cc.range.end = max;
595 cc.constructing = false;
596
597 self.left_padding -= cc.padding.left;
599 self.right_padding -= cc.padding.right;
600 }
601 }
602 }
603
604 pub fn widgets(&mut self, list: impl IntoIterator<Item = (W, FormLabel, FormWidget)>) {
606 for (k, l, w) in list {
607 self.widget(k, l, w);
608 }
609 }
610
611 pub fn widget(&mut self, key: W, label: FormLabel, widget: FormWidget) {
614 let (label, label_str) = match label {
616 FormLabel::Str(s) => {
617 let width = unicode_display_width::width(s) as u16;
618 (FormLabel::Width(width), Some(Cow::Borrowed(s)))
619 }
620 FormLabel::String(s) => {
621 let width = unicode_display_width::width(&s) as u16;
622 (FormLabel::Width(width), Some(Cow::Owned(s)))
623 }
624 FormLabel::Width(w) => (FormLabel::Width(w), None),
625 FormLabel::Size(w, h) => (FormLabel::Size(w, h), None),
626 FormLabel::None => (FormLabel::None, None),
627 };
628
629 let w = match &label {
630 FormLabel::None => 0,
631 FormLabel::Str(_) | FormLabel::String(_) => {
632 unreachable!()
633 }
634 FormLabel::Width(w) => *w,
635 FormLabel::Size(w, _) => *w,
636 };
637 self.min_label = max(self.min_label, w);
638
639 let (w, ww) = match &widget {
640 FormWidget::None => (0, 0),
641 FormWidget::Width(w) => (*w, 0),
642 FormWidget::Size(w, _) => (*w, 0),
643 FormWidget::StretchY(w, _) => (*w, 0),
644 FormWidget::Wide(w, _) => (0, *w),
645 FormWidget::StretchX(w, _) => (*w, 0),
646 FormWidget::WideStretchX(w, _) => (0, *w),
647 FormWidget::StretchXY(w, _) => (*w, 0),
648 FormWidget::WideStretchXY(w, _) => (0, *w),
649 };
650 self.min_widget = max(self.min_widget, w);
651 self.min_widget_wide = max(self.min_widget_wide, ww);
652
653 self.widgets.push(WidgetDef {
654 id: key,
655 label,
656 label_str,
657 widget,
658 gap: 0,
659 });
660 }
661
662 pub fn gap(&mut self, n: u16) {
664 self.widgets.last_mut().expect("widget").gap = n;
665 }
666
667 pub fn page_break(&mut self) {
671 self.page_breaks.push(self.widgets.len() - 1);
672 }
673
674 pub fn column_break(&mut self) {
679 self.page_breaks.push(self.widgets.len() - 1);
680 }
681
682 fn validate_containers(&self) {
683 for cc in self.blocks.iter() {
684 if cc.constructing {
685 panic!("Unclosed container {:?}", cc.id);
686 }
687 }
688 }
689
690 #[inline(always)]
692 pub fn build_endless(self, width: u16) -> GenericLayout<W> {
693 self.validate_containers();
694 build_layout::<W, true>(self, Size::new(width, u16::MAX))
695 }
696
697 #[inline(always)]
699 pub fn build_paged(self, page: Size) -> GenericLayout<W> {
700 self.validate_containers();
701 build_layout::<W, false>(self, page)
702 }
703}
704
705impl XPositions {
706 fn new(page: &Page, column: u16, mirror: bool) -> XPositions {
707 let border = if mirror {
708 Padding::new(
709 page.def.page_border.right,
710 page.def.page_border.left,
711 page.def.page_border.top,
712 page.def.page_border.bottom,
713 )
714 } else {
715 page.def.page_border
716 };
717
718 let layout_width = page
719 .def
720 .full_width
721 .saturating_sub(border.left)
722 .saturating_sub(border.right);
723 let n_col_spacers = page.def.columns.saturating_sub(1);
724 let column_width =
725 layout_width.saturating_sub(page.def.column_spacing * n_col_spacers) / page.def.columns;
726 let right_margin = page.def.full_width.saturating_sub(border.right);
727
728 let offset;
729 let label_left;
730 let widget_left;
731 let container_left;
732 let container_right;
733 let widget_right;
734
735 match page.def.flex {
736 Flex::Legacy => {
737 offset = border.left + (column_width + page.def.column_spacing) * column;
738 label_left = page.def.max_left_padding;
739 widget_left = label_left + page.def.max_label + page.def.spacing;
740 widget_right = column_width.saturating_sub(page.def.max_right_padding);
741
742 container_left = 0;
743 container_right = column_width;
744 }
745 Flex::Start => {
746 let single_width = page.def.max_left_padding
747 + page.def.max_label
748 + page.def.spacing
749 + page.def.max_widget
750 + page.def.max_right_padding
751 + page.def.column_spacing;
752
753 offset = border.left + single_width * column;
754 label_left = page.def.max_left_padding;
755 widget_left = label_left + page.def.max_label + page.def.spacing;
756 widget_right = widget_left + page.def.max_widget;
757
758 container_left = 0;
759 container_right = widget_right + page.def.max_right_padding;
760 }
761 Flex::Center => {
762 let single_width = page.def.max_left_padding
763 + page.def.max_label
764 + page.def.spacing
765 + page.def.max_widget
766 + page.def.max_right_padding
767 + page.def.column_spacing;
768 let rest = layout_width
769 .saturating_sub(single_width * page.def.columns)
770 .saturating_add(page.def.column_spacing);
771
772 offset = border.left + rest / 2 + single_width * column;
773 label_left = page.def.max_left_padding;
774 widget_left = label_left + page.def.max_label + page.def.spacing;
775 widget_right = widget_left + page.def.max_widget;
776
777 container_left = 0;
778 container_right = widget_right + page.def.max_right_padding;
779 }
780 Flex::End => {
781 let single_width = page.def.max_left_padding
782 + page.def.max_label
783 + page.def.spacing
784 + page.def.max_widget
785 + page.def.max_right_padding
786 + page.def.column_spacing;
787
788 offset = right_margin
789 .saturating_sub(single_width * (page.def.columns - column))
790 .saturating_add(page.def.column_spacing);
791 label_left = page.def.max_left_padding;
792 widget_left = label_left + page.def.max_label + page.def.spacing;
793 widget_right = widget_left + page.def.max_widget;
794
795 container_left = 0;
796 container_right = widget_right + page.def.max_right_padding;
797 }
798 Flex::SpaceAround | Flex::SpaceEvenly => {
799 let single_width = page.def.max_left_padding
800 + page.def.max_label
801 + page.def.spacing
802 + page.def.max_widget
803 + page.def.max_right_padding;
804 let rest = layout_width.saturating_sub(single_width * page.def.columns);
805 let spacing = rest / (page.def.columns + 1);
806
807 offset = border.left + spacing + (single_width + spacing) * column;
808 label_left = page.def.max_left_padding;
809 widget_left = label_left + page.def.max_label + page.def.spacing;
810 widget_right = widget_left + page.def.max_widget;
811
812 container_left = 0;
813 container_right = widget_right + page.def.max_right_padding;
814 }
815 Flex::SpaceBetween => {
816 let single_width = page.def.max_left_padding
817 + page.def.max_label
818 + page.def.max_widget
819 + page.def.max_right_padding;
820 let rest = layout_width.saturating_sub(single_width * page.def.columns);
821 let spacing = if page.def.columns > 1 {
822 rest / (page.def.columns - 1)
823 } else {
824 0
825 };
826
827 offset = border.left + (single_width + spacing) * column;
828 label_left = page.def.max_left_padding;
829 widget_left = label_left + page.def.max_label + page.def.spacing;
830 widget_right = widget_left + page.def.max_widget;
831
832 container_left = 0;
833 container_right = widget_right + page.def.max_right_padding;
834 }
835 }
836
837 XPositions {
838 label_left: offset + label_left,
839 label_width: page.def.max_label,
840 widget_left: offset + widget_left,
841 widget_width: page.def.max_widget,
842 widget_right: offset + widget_right,
843 container_left: offset + container_left,
844 container_right: offset + container_right,
845 total_width: widget_right - label_left,
846 }
847 }
848}
849
850impl Page {
851 fn adjusted_widths<W>(layout: &LayoutForm<W>, page_size: Size) -> (u16, u16, u16)
852 where
853 W: Eq + Hash + Clone + Debug,
854 {
855 let layout_width = page_size
856 .width
857 .saturating_sub(layout.page_border.left)
858 .saturating_sub(layout.page_border.right);
859 let n_col_spacers = layout.columns.saturating_sub(1);
860 let column_width =
861 layout_width.saturating_sub(layout.column_spacing * n_col_spacers) / layout.columns;
862
863 let mut min_label = layout.min_label;
864 let mut min_widget = max(
865 layout.min_widget,
866 layout
867 .min_widget_wide
868 .saturating_sub(layout.min_label)
869 .saturating_sub(layout.spacing),
870 );
871 let mut spacing = layout.spacing;
872
873 let nominal =
874 layout.max_left_padding + min_label + spacing + min_widget + layout.max_right_padding;
875
876 if nominal > column_width {
877 let mut reduce = nominal - column_width;
878
879 if min_label > 5 && min_label - 5 > reduce {
880 if spacing.saturating_sub(1) > reduce {
883 spacing -= reduce;
884 reduce = 0;
885 } else {
886 reduce -= spacing.saturating_sub(1);
887 spacing = if spacing > 0 { 1 } else { 0 };
888 }
889 } else {
890 if spacing > reduce {
891 spacing -= reduce;
892 reduce = 0;
893 } else {
894 reduce -= spacing;
895 spacing = 0;
896 }
897 }
898 if min_label > 5 {
899 if min_label - 5 > reduce {
900 min_label -= reduce;
901 reduce = 0;
902 } else {
903 reduce -= min_label - 5;
904 min_label = 5;
905 }
906 }
907 if min_widget > 5 {
908 if min_widget - 5 > reduce {
909 min_widget -= reduce;
910 reduce = 0;
911 } else {
912 reduce -= min_widget - 5;
913 min_widget = 5;
914 }
915 }
916 if min_label > reduce {
917 min_label -= reduce;
918 reduce = 0;
919 } else {
920 reduce -= min_label;
921 min_label = 0;
922 }
923 if min_widget > reduce {
924 min_widget -= reduce;
925 } else {
927 min_widget = 0;
929 }
930 }
931
932 (min_label, spacing, min_widget)
933 }
934
935 fn new<W>(layout: &LayoutForm<W>, page_size: Size) -> Self
936 where
937 W: Eq + Hash + Clone + Debug,
938 {
939 let (max_label, spacing, max_widget) = Self::adjusted_widths(layout, page_size);
940
941 let def = PageDef {
942 page_border: layout.page_border,
943 full_width: page_size.width,
944 flex: layout.flex,
945 max_left_padding: layout.max_left_padding,
946 max_right_padding: layout.max_right_padding,
947 max_label,
948 max_widget,
949 width: page_size.width,
950 height: page_size.height,
951 top: layout.page_border.top,
952 bottom: layout.page_border.bottom,
953 columns: layout.columns,
954 column_spacing: layout.column_spacing,
955 spacing,
956 line_spacing: layout.line_spacing,
957 };
958 let mut s = Page {
959 def: Rc::new(def),
960 page_no: 0,
961 page_start: 0,
962 page_end: page_size.height.saturating_sub(layout.page_border.bottom),
963 y: layout.page_border.top,
964 top_padding: 0,
965 bottom_padding: 0,
966 bottom_padding_break: 0,
967 effective_line_spacing: 0,
968 x_pos: Default::default(),
969 };
970 s.x_pos = XPositions::new(&s, 0, false);
971 s
972 }
973}
974
975fn adjust_blocks<W>(layout: &mut LayoutForm<W>, page_height: u16)
977where
978 W: Eq + Hash + Clone + Debug,
979{
980 if page_height == u16::MAX {
981 return;
982 }
983
984 if page_height < 3 {
985 for block_def in layout.blocks.iter_mut() {
986 if let Some(block) = block_def.block.as_mut() {
987 let padding = block_padding2(block);
988 let borders = if padding.left > 0 {
989 Borders::LEFT
990 } else {
991 Borders::NONE
992 } | if padding.right > 0 {
993 Borders::RIGHT
994 } else {
995 Borders::NONE
996 };
997
998 *block = mem::take(block).borders(borders);
999 block_def.padding.top = 0;
1000 block_def.padding.bottom = 0;
1001 }
1002 }
1003 }
1004}
1005
1006fn build_layout<W, const ENDLESS: bool>(
1008 mut layout: LayoutForm<W>,
1009 page_size: Size,
1010) -> GenericLayout<W>
1011where
1012 W: Eq + Hash + Clone + Debug,
1013{
1014 let mut gen_layout = GenericLayout::with_capacity(
1015 layout.widgets.len(), layout.blocks.len() * 2,
1017 );
1018 gen_layout.set_page_size(page_size);
1019
1020 adjust_blocks(&mut layout, page_size.height);
1022
1023 let mut stretch = Vec::with_capacity(layout.widgets.len());
1025 let mut blocks_out = Vec::with_capacity(layout.blocks.len());
1026
1027 let mut saved_page;
1028 let mut page = Page::new(&layout, page_size);
1029
1030 for (idx, widget) in layout.widgets.iter_mut().enumerate() {
1031 saved_page = page.clone();
1033
1034 let mut label_area;
1035 let mut widget_area;
1036 let mut gap;
1037
1038 (label_area, widget_area, gap) =
1039 next_widget(&mut page, &mut layout.blocks, widget, idx, false);
1040 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1041
1042 if !ENDLESS && page.y.saturating_add(page.bottom_padding_break) > page.page_end {
1044 page = saved_page;
1046
1047 blocks_out.clear();
1049 page_break_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1050 push_blocks(&mut blocks_out, &mut gen_layout);
1051 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1052 page_break::<ENDLESS>(&mut page);
1053 assert!(stretch.is_empty());
1054
1055 (label_area, widget_area, gap) =
1057 next_widget(&mut page, &mut layout.blocks, widget, idx, true);
1058 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1059 }
1060
1061 if !ENDLESS && widget.widget.is_stretch_y() {
1063 stretch.push(gen_layout.widget_len());
1064 }
1065
1066 gen_layout.add(
1068 widget.id.clone(),
1069 widget_area,
1070 widget.label_str.take(),
1071 label_area,
1072 );
1073
1074 push_blocks(&mut blocks_out, &mut gen_layout);
1075
1076 let break_gap = !ENDLESS
1077 && page
1078 .y
1079 .saturating_add(gap)
1080 .saturating_add(page.bottom_padding_break)
1081 > page.page_end;
1082 let break_manual = layout.page_breaks.contains(&idx);
1083
1084 if break_gap || break_manual {
1086 assert!(blocks_out.is_empty());
1087 page_break_blocks(&mut page, &mut layout.blocks, idx + 1, &mut blocks_out);
1088 push_blocks(&mut blocks_out, &mut gen_layout);
1089 if !ENDLESS {
1090 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1091 }
1092 page_break::<ENDLESS>(&mut page);
1093 assert!(stretch.is_empty());
1094 }
1095
1096 if !break_gap {
1097 page.y += gap;
1098 }
1099
1100 drop_blocks(&mut layout.blocks, idx);
1101 assert!(blocks_out.is_empty());
1102 }
1103
1104 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1106
1107 gen_layout.set_page_count(((page.page_no + page.def.columns) / page.def.columns) as usize);
1108 gen_layout
1109}
1110
1111fn drop_blocks(_block_def: &mut VecDeque<BlockDef>, _idx: usize) {
1114 }
1127
1128fn page_break_blocks(
1129 page: &mut Page,
1130 block_def: &mut VecDeque<BlockDef>,
1131 idx: usize,
1132 blocks_out: &mut Vec<BlockOut>,
1133) {
1134 for block in block_def.iter_mut().rev() {
1137 if idx > block.range.start && idx < block.range.end {
1138 end_block(page, block);
1139 blocks_out.push(block.as_out());
1140
1141 block.range.start = idx;
1143 }
1144 }
1148}
1149
1150fn page_break<const ENDLESS: bool>(page: &mut Page) {
1152 page.page_no += 1;
1154
1155 let column = page.page_no % page.def.columns;
1156 let mirror = (page.page_no / page.def.columns) % 2 == 1;
1157
1158 if !ENDLESS {
1159 page.page_start = (page.page_no / page.def.columns).saturating_mul(page.def.height);
1160 page.page_end = page
1161 .page_start
1162 .saturating_add(page.def.height.saturating_sub(page.def.bottom));
1163 }
1164
1165 page.x_pos = XPositions::new(page, column, mirror);
1166 page.y = page.page_start.saturating_add(page.def.top);
1167
1168 page.effective_line_spacing = 0;
1169 page.top_padding = 0;
1170 page.bottom_padding = 0;
1171 page.bottom_padding_break = 0;
1172}
1173
1174fn next_widget<W>(
1176 page: &mut Page,
1177 block_def: &mut VecDeque<BlockDef>,
1178 widget: &WidgetDef<W>,
1179 idx: usize,
1180 must_fit: bool,
1181) -> (Rect, Rect, u16)
1182where
1183 W: Eq + Hash + Clone + Debug,
1184{
1185 page.y = page.y.saturating_add(page.effective_line_spacing);
1187
1188 page.effective_line_spacing = page.def.line_spacing;
1189 page.top_padding = 0;
1190 page.bottom_padding = 0;
1191 page.bottom_padding_break = 0;
1192
1193 for block in block_def.iter_mut() {
1195 if block.range.start == idx {
1196 start_block(page, block);
1197 }
1198 if block.range.start <= idx {
1199 widget_padding(page, idx, block);
1200 }
1201 }
1202
1203 let (label_area, widget_area, advance, gap) = areas_and_advance(page, widget, must_fit);
1205
1206 page.y = page.y.saturating_add(advance);
1207
1208 (label_area, widget_area, gap)
1209}
1210
1211fn start_block(page: &mut Page, block: &mut BlockDef) {
1213 block.area.x = page.x_pos.container_left;
1215 block.area.width = page
1216 .x_pos
1217 .container_right
1218 .saturating_sub(page.x_pos.container_left);
1219 block.area.y = page.y;
1220
1221 page.y = page.y.saturating_add(block.padding.top);
1223 page.top_padding += block.padding.top;
1224 page.x_pos.container_left = page.x_pos.container_left.saturating_add(block.padding.left);
1225 page.x_pos.container_right = page
1226 .x_pos
1227 .container_right
1228 .saturating_sub(block.padding.right);
1229}
1230
1231fn widget_padding(page: &mut Page, idx: usize, block: &mut BlockDef) {
1232 if block.range.end > idx + 1 {
1233 page.bottom_padding_break += block.padding.bottom;
1234 } else if block.range.end == idx + 1 {
1235 page.bottom_padding += block.padding.bottom;
1236 }
1237}
1238
1239fn next_blocks(
1240 page: &mut Page,
1241 block_def: &mut VecDeque<BlockDef>,
1242 idx: usize,
1243 blocks_out: &mut Vec<BlockOut>,
1244) {
1245 for block in block_def.iter_mut().rev() {
1248 if idx + 1 == block.range.end {
1249 end_block(page, block);
1250 blocks_out.push(block.as_out());
1251 }
1252 }
1256}
1257
1258fn push_blocks<W: Eq + Hash + Clone>(
1259 blocks_out: &mut Vec<BlockOut>,
1260 gen_layout: &mut GenericLayout<W>,
1261) {
1262 while let Some(cc) = blocks_out.pop() {
1263 gen_layout.add_block(cc.area, cc.block);
1264 }
1265}
1266
1267fn end_block(page: &mut Page, block: &mut BlockDef) {
1269 page.y = page.y.saturating_add(block.padding.bottom);
1271 page.x_pos.container_left = page.x_pos.container_left.saturating_sub(block.padding.left);
1272 page.x_pos.container_right = page
1273 .x_pos
1274 .container_right
1275 .saturating_add(block.padding.right);
1276
1277 block.area.height = page.y.saturating_sub(block.area.y);
1279}
1280
1281fn areas_and_advance<W: Debug + Clone>(
1284 page: &Page,
1285 widget: &WidgetDef<W>,
1286 must_fit: bool,
1287) -> (Rect, Rect, u16, u16) {
1288 let stacked = matches!(
1293 widget.widget,
1294 FormWidget::Wide(_, _) | FormWidget::WideStretchX(_, _) | FormWidget::WideStretchXY(_, _)
1295 );
1296
1297 let mut label_height = match &widget.label {
1298 FormLabel::None => 0,
1299 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1300 FormLabel::Width(_) => 1,
1301 FormLabel::Size(_, h) => *h,
1302 };
1303
1304 let mut widget_height = match &widget.widget {
1305 FormWidget::None => 0,
1306 FormWidget::Width(_) => 1,
1307 FormWidget::Size(_, h) => *h,
1308 FormWidget::StretchY(_, h) => *h,
1309 FormWidget::Wide(_, h) => *h,
1310 FormWidget::StretchX(_, h) => *h,
1311 FormWidget::WideStretchX(_, h) => *h,
1312 FormWidget::StretchXY(_, h) => *h,
1313 FormWidget::WideStretchXY(_, h) => *h,
1314 };
1315
1316 let gap_height = widget.gap;
1317
1318 let stretch_width = page
1319 .x_pos
1320 .widget_right
1321 .saturating_sub(page.x_pos.widget_left);
1322 let total_stretch_width = page
1323 .x_pos
1324 .widget_right
1325 .saturating_sub(page.x_pos.label_left);
1326
1327 let max_height = if !must_fit {
1328 page.def
1329 .height
1330 .saturating_sub(page.def.top)
1331 .saturating_sub(page.def.bottom)
1332 .saturating_sub(page.top_padding)
1333 .saturating_sub(page.bottom_padding)
1334 } else {
1335 page.def
1336 .height
1337 .saturating_sub(page.y - page.page_start)
1338 .saturating_sub(page.def.bottom)
1339 .saturating_sub(page.bottom_padding_break)
1340 };
1341
1342 if stacked {
1343 if label_height + widget_height > max_height {
1344 label_height = max(1, max_height.saturating_sub(widget_height));
1345 }
1346 if label_height + widget_height > max_height {
1347 widget_height = max(1, max_height.saturating_sub(label_height));
1348 }
1349 if label_height + widget_height > max_height {
1350 label_height = 0;
1351 }
1352 if label_height + widget_height > max_height {
1353 widget_height = max_height;
1354 }
1355
1356 let mut label_area = match &widget.label {
1357 FormLabel::None => Rect::new(
1358 page.x_pos.label_left, page.y,
1360 0,
1361 0,
1362 ),
1363 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1364 FormLabel::Width(_) => Rect::new(
1365 page.x_pos.label_left,
1366 page.y,
1367 page.x_pos.label_width,
1368 label_height,
1369 ),
1370 FormLabel::Size(_, _) => Rect::new(
1371 page.x_pos.label_left,
1372 page.y,
1373 page.x_pos.label_width,
1374 label_height,
1375 ),
1376 };
1377 match &widget.widget {
1378 FormWidget::Wide(_, _) => label_area.width = page.x_pos.total_width,
1379 FormWidget::WideStretchX(_, _) => label_area.width = total_stretch_width,
1380 FormWidget::WideStretchXY(_, _) => label_area.width = total_stretch_width,
1381 _ => {}
1382 }
1383
1384 let widget_area = match &widget.widget {
1385 FormWidget::None => unreachable!(),
1386 FormWidget::Width(_) => unreachable!(),
1387 FormWidget::Size(_, _) => unreachable!(),
1388 FormWidget::StretchY(_, _) => unreachable!(),
1389 FormWidget::Wide(w, _) => Rect::new(
1390 page.x_pos.label_left,
1391 page.y + label_height,
1392 min(*w, page.x_pos.total_width),
1393 widget_height,
1394 ),
1395 FormWidget::StretchX(_, _) => unreachable!(),
1396 FormWidget::WideStretchX(_, _) => Rect::new(
1397 page.x_pos.label_left,
1398 page.y + label_height,
1399 total_stretch_width,
1400 widget_height,
1401 ),
1402 FormWidget::StretchXY(_, _) => unreachable!(),
1403 FormWidget::WideStretchXY(_, _) => Rect::new(
1404 page.x_pos.label_left,
1405 page.y + label_height,
1406 total_stretch_width,
1407 widget_height,
1408 ),
1409 };
1410
1411 (
1412 label_area,
1413 widget_area,
1414 label_area.height + widget_area.height,
1415 gap_height,
1416 )
1417 } else {
1418 label_height = min(label_height, max_height);
1419 widget_height = min(widget_height, max_height);
1420 let height = max(label_height, widget_height);
1421
1422 let label_area = match &widget.label {
1423 FormLabel::None => Rect::new(
1424 page.x_pos.label_left, page.y,
1426 0,
1427 0,
1428 ),
1429 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1430 FormLabel::Width(_) => Rect::new(
1431 page.x_pos.label_left,
1432 page.y,
1433 page.x_pos.label_width,
1434 height,
1435 ),
1436 FormLabel::Size(_, _) => Rect::new(
1437 page.x_pos.label_left,
1438 page.y,
1439 page.x_pos.label_width,
1440 height,
1441 ),
1442 };
1443
1444 let widget_area = match &widget.widget {
1445 FormWidget::None => Rect::default(),
1446 FormWidget::Width(w) => Rect::new(
1447 page.x_pos.widget_left,
1448 page.y,
1449 min(*w, page.x_pos.widget_width),
1450 height,
1451 ),
1452 FormWidget::Size(w, _) => Rect::new(
1453 page.x_pos.widget_left,
1454 page.y,
1455 min(*w, page.x_pos.widget_width),
1456 height,
1457 ),
1458 FormWidget::StretchY(w, _) => Rect::new(
1459 page.x_pos.widget_left,
1460 page.y,
1461 min(*w, page.x_pos.widget_width),
1462 height,
1463 ),
1464 FormWidget::Wide(_, _) => unreachable!(),
1465 FormWidget::StretchX(_, _) => Rect::new(
1466 page.x_pos.widget_left, page.y,
1468 stretch_width,
1469 height,
1470 ),
1471 FormWidget::WideStretchX(_, _) => unreachable!(),
1472 FormWidget::StretchXY(_, _) => Rect::new(
1473 page.x_pos.widget_left, page.y,
1475 stretch_width,
1476 height,
1477 ),
1478 FormWidget::WideStretchXY(_, _) => unreachable!(),
1479 };
1480
1481 (
1482 label_area,
1483 widget_area,
1484 max(label_area.height, widget_area.height),
1485 gap_height,
1486 )
1487 }
1488}
1489
1490fn adjust_y_stretch<W: Eq + Hash + Clone>(
1493 page: &Page,
1494 stretch_y: &mut Vec<usize>,
1495 gen_layout: &mut GenericLayout<W>,
1496) {
1497 let mut remainder = page.page_end.saturating_sub(page.y);
1498 if remainder == 0 {
1499 stretch_y.clear();
1500 return;
1501 }
1502
1503 let mut n = stretch_y.len() as u16;
1504 for y_idx in stretch_y.drain(..) {
1505 let stretch = remainder / n;
1508 remainder -= stretch;
1509 n -= 1;
1510
1511 let mut area = gen_layout.widget(y_idx);
1513 let test_y = area.bottom();
1514 let test_x = page.x_pos.container_left;
1515
1516 area.height += stretch;
1517 gen_layout.set_widget(y_idx, area);
1518
1519 for idx in y_idx + 1..gen_layout.widget_len() {
1521 let mut area = gen_layout.widget(idx);
1522 if area.y >= test_y {
1523 area.y += stretch;
1524 }
1525 gen_layout.set_widget(idx, area);
1526
1527 let mut area = gen_layout.label(idx);
1528 if area.y >= test_y {
1529 area.y += stretch;
1530 }
1531 gen_layout.set_label(idx, area);
1532 }
1533
1534 for idx in 0..gen_layout.block_len() {
1536 let mut area = gen_layout.block_area(idx);
1537 if area.x >= test_x && area.y >= test_y {
1538 area.y += stretch;
1539 }
1540 if area.x >= test_x && area.y <= test_y && area.bottom() > test_y {
1542 area.height += stretch;
1543 }
1544 gen_layout.set_block_area(idx, area);
1545 }
1546 }
1547}