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