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)]
717 #[deprecated(since = "1.2.0", note = "use build_endless")]
718 pub fn endless(mut self, width: u16, border: Padding) -> GenericLayout<W> {
719 self.page_border = border;
720 self.build_endless(width)
721 }
722
723 #[inline(always)]
725 #[deprecated(since = "1.2.0", note = "use build_paged")]
726 pub fn paged(mut self, page: Size, border: Padding) -> GenericLayout<W> {
727 self.page_border = border;
728 self.build_paged(page)
729 }
730
731 #[inline(always)]
733 pub fn build_endless(self, width: u16) -> GenericLayout<W> {
734 self.validate_containers();
735 build_layout(self, Size::new(width, u16::MAX), true)
736 }
737
738 #[inline(always)]
740 pub fn build_paged(self, page: Size) -> GenericLayout<W> {
741 self.validate_containers();
742 build_layout(self, page, false)
743 }
744}
745
746impl XPositions {
747 fn new(page: &Page, column: u16, mirror: bool) -> XPositions {
748 let border = if mirror {
749 Padding::new(
750 page.page_border.right,
751 page.page_border.left,
752 page.page_border.top,
753 page.page_border.bottom,
754 )
755 } else {
756 page.page_border
757 };
758
759 let layout_width = page
760 .full_width
761 .saturating_sub(border.left)
762 .saturating_sub(border.right);
763 let column_width = (layout_width / page.columns).saturating_sub(page.column_spacing);
764 let right_margin = page.full_width.saturating_sub(border.right);
765
766 let offset;
767 let label_left;
768 let widget_left;
769 let container_left;
770 let container_right;
771 let widget_right;
772
773 match page.flex {
774 Flex::Legacy => {
775 offset = border.left + (column_width + page.column_spacing) * column;
776 label_left = page.max_left_padding;
777 widget_left = label_left + page.max_label + page.spacing;
778 widget_right = column_width.saturating_sub(page.max_right_padding);
779
780 container_left = 0;
781 container_right = column_width;
782 }
783 Flex::Start => {
784 let single_width = page.max_left_padding
785 + page.max_label
786 + page.spacing
787 + page.max_widget
788 + page.max_right_padding
789 + page.column_spacing;
790
791 offset = border.left + single_width * column;
792 label_left = page.max_left_padding;
793 widget_left = label_left + page.max_label + page.spacing;
794 widget_right = widget_left + page.max_widget;
795
796 container_left = 0;
797 container_right = widget_right + page.max_right_padding;
798 }
799 Flex::Center => {
800 let single_width = page.max_left_padding
801 + page.max_label
802 + page.spacing
803 + page.max_widget
804 + page.max_right_padding
805 + page.column_spacing;
806 let rest = layout_width
807 .saturating_sub(single_width * page.columns)
808 .saturating_add(page.column_spacing);
809
810 offset = border.left + rest / 2 + single_width * column;
811 label_left = page.max_left_padding;
812 widget_left = label_left + page.max_label + page.spacing;
813 widget_right = widget_left + page.max_widget;
814
815 container_left = 0;
816 container_right = widget_right + page.max_right_padding;
817 }
818 Flex::End => {
819 let single_width = page.max_left_padding
820 + page.max_label
821 + page.spacing
822 + page.max_widget
823 + page.max_right_padding
824 + page.column_spacing;
825
826 offset = right_margin
827 .saturating_sub(single_width * (page.columns - column))
828 .saturating_add(page.column_spacing);
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::SpaceAround => {
837 let single_width = page.max_left_padding
838 + page.max_label
839 + page.spacing
840 + page.max_widget
841 + page.max_right_padding;
842 let rest = layout_width.saturating_sub(single_width * page.columns);
843 let spacing = rest / (page.columns + 1);
844
845 offset = border.left + spacing + (single_width + spacing) * column;
846 label_left = page.max_left_padding;
847 widget_left = label_left + page.max_label + page.spacing;
848 widget_right = widget_left + page.max_widget;
849
850 container_left = 0;
851 container_right = widget_right + page.max_right_padding;
852 }
853 Flex::SpaceBetween => {
854 let single_width = page.max_left_padding
855 + page.max_label
856 + page.max_widget
857 + page.max_right_padding;
858 let rest = layout_width.saturating_sub(single_width * page.columns);
859 let spacing = if page.columns > 1 {
860 rest / (page.columns - 1)
861 } else {
862 0
863 };
864
865 offset = border.left + (single_width + spacing) * column;
866 label_left = page.max_left_padding;
867 widget_left = label_left + page.max_label + page.spacing;
868 widget_right = widget_left + page.max_widget;
869
870 container_left = 0;
871 container_right = widget_right + page.max_right_padding;
872 }
873 }
874
875 XPositions {
876 container_left: offset + container_left,
877 label_left: offset + label_left,
878 label_width: page.max_label,
879 widget_left: offset + widget_left,
880 widget_width: page.max_widget,
881 container_right: offset + container_right,
882 total_width: widget_right - label_left,
883 widget_right: offset + widget_right,
884 }
885 }
886}
887
888impl Page {
889 fn adjusted_widths<W>(layout: &LayoutForm<W>, page_size: Size) -> (u16, u16, u16)
890 where
891 W: Eq + Hash + Clone + Debug,
892 {
893 let layout_width = page_size
894 .width
895 .saturating_sub(layout.page_border.left)
896 .saturating_sub(layout.page_border.right);
897 let column_width = (layout_width / layout.columns).saturating_sub(layout.column_spacing);
898
899 let mut max_label = layout.max_label;
900 let mut max_widget = layout.max_widget;
901 let mut spacing = layout.spacing;
902
903 let nominal =
904 layout.max_left_padding + max_label + spacing + max_widget + layout.max_right_padding;
905
906 if nominal > column_width {
907 let mut reduce = nominal - column_width;
908
909 if spacing > reduce {
910 spacing -= reduce;
911 reduce = 0;
912 } else {
913 reduce -= spacing;
914 spacing = 0;
915 }
916 if max_label > 5 {
917 if max_label - 5 > reduce {
918 max_label -= reduce;
919 reduce = 0;
920 } else {
921 reduce -= max_label - 5;
922 max_label = 5;
923 }
924 }
925 if max_widget > 5 {
926 if max_widget - 5 > reduce {
927 max_widget -= reduce;
928 reduce = 0;
929 } else {
930 reduce -= max_widget - 5;
931 max_widget = 5;
932 }
933 }
934 if max_label > reduce {
935 max_label -= reduce;
936 reduce = 0;
937 } else {
938 reduce -= max_label;
939 max_label = 0;
940 }
941 if max_widget > reduce {
942 max_widget -= reduce;
943 } else {
945 max_widget = 0;
947 }
948 }
949
950 (max_label, spacing, max_widget)
951 }
952
953 fn new<W>(layout: &LayoutForm<W>, page_size: Size) -> Self
954 where
955 W: Eq + Hash + Clone + Debug,
956 {
957 let (max_label, spacing, max_widget) = Self::adjusted_widths(layout, page_size);
958
959 let mut s = Self {
960 page_border: layout.page_border,
961 full_width: page_size.width,
962 flex: layout.flex,
963 max_left_padding: layout.max_left_padding,
964 max_right_padding: layout.max_right_padding,
965 max_label,
966 max_widget,
967 width: page_size.width,
968 height: page_size.height,
969 top: layout.page_border.top,
970 bottom: layout.page_border.bottom,
971 columns: layout.columns,
972 column_spacing: layout.column_spacing,
973 spacing,
974 line_spacing: layout.line_spacing,
975 page_no: 0,
976 page_start: 0,
977 page_end: page_size.height.saturating_sub(layout.page_border.bottom),
978 y: layout.page_border.top,
979 top_padding: 0,
980 bottom_padding: 0,
981 bottom_padding_break: 0,
982 effective_line_spacing: 0,
983 x_pos: Default::default(),
984 };
985 s.x_pos = XPositions::new(&s, 0, false);
986 s
987 }
988}
989
990fn adjust_blocks<W>(layout: &mut LayoutForm<W>, page_height: u16)
992where
993 W: Eq + Hash + Clone + Debug,
994{
995 if page_height == u16::MAX {
996 return;
997 }
998
999 if page_height < 3 {
1000 for block_def in layout.blocks.iter_mut() {
1001 if let Some(block) = block_def.block.as_mut() {
1002 let padding = block_padding2(block);
1003 let borders = if padding.left > 0 {
1004 Borders::LEFT
1005 } else {
1006 Borders::NONE
1007 } | if padding.right > 0 {
1008 Borders::RIGHT
1009 } else {
1010 Borders::NONE
1011 };
1012
1013 *block = mem::take(block).borders(borders);
1014 block_def.padding.top = 0;
1015 block_def.padding.bottom = 0;
1016 }
1017 }
1018 }
1019}
1020
1021fn build_layout<W>(mut layout: LayoutForm<W>, page_size: Size, endless: bool) -> GenericLayout<W>
1023where
1024 W: Eq + Hash + Clone + Debug,
1025{
1026 let mut gen_layout = GenericLayout::with_capacity(
1027 layout.widgets.len(), layout.blocks.len() * 2,
1029 );
1030 gen_layout.set_page_size(page_size);
1031
1032 adjust_blocks(&mut layout, page_size.height);
1034
1035 let mut stretch = Vec::with_capacity(layout.widgets.len());
1037 let mut blocks_out = Vec::with_capacity(layout.blocks.len());
1038
1039 let mut saved_page;
1040 let mut page = Page::new(&layout, page_size);
1041
1042 for (idx, widget) in layout.widgets.iter_mut().enumerate() {
1043 saved_page = page;
1045
1046 let mut label_area;
1047 let mut widget_area;
1048
1049 (label_area, widget_area) = next_widget(&mut page, &mut layout.blocks, widget, idx, false);
1050 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1051
1052 if !endless && page.y.saturating_add(page.bottom_padding_break) > page.page_end {
1054 page = saved_page;
1056
1057 blocks_out.clear();
1059 page_break_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1060 push_blocks(&mut blocks_out, &mut gen_layout);
1061 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1062 page_break(&mut page);
1063 assert!(stretch.is_empty());
1064
1065 (label_area, widget_area) =
1067 next_widget(&mut page, &mut layout.blocks, widget, idx, true);
1068 next_blocks(&mut page, &mut layout.blocks, idx, &mut blocks_out);
1069 }
1070
1071 if !endless && widget.widget.is_stretch_y() {
1073 stretch.push(gen_layout.widget_len());
1074 }
1075
1076 gen_layout.add(
1078 widget.id.clone(),
1079 widget_area,
1080 widget.label_str.take(),
1081 label_area,
1082 );
1083
1084 push_blocks(&mut blocks_out, &mut gen_layout);
1085
1086 if !endless && layout.page_breaks.contains(&idx) {
1088 assert!(blocks_out.is_empty());
1089 page_break_blocks(&mut page, &mut layout.blocks, idx + 1, &mut blocks_out);
1090 push_blocks(&mut blocks_out, &mut gen_layout);
1091 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1092 page_break(&mut page);
1093 assert!(stretch.is_empty());
1094 }
1095
1096 drop_blocks(&mut layout.blocks, idx);
1097 assert!(blocks_out.is_empty());
1098 }
1099
1100 adjust_y_stretch(&page, &mut stretch, &mut gen_layout);
1102
1103 gen_layout.set_page_count(((page.page_no + page.columns) / page.columns) as usize);
1104 gen_layout
1105}
1106
1107fn drop_blocks(_block_def: &mut VecDeque<BlockDef>, _idx: usize) {
1110 }
1123
1124fn page_break_blocks(
1125 page: &mut Page,
1126 block_def: &mut VecDeque<BlockDef>,
1127 idx: usize,
1128 blocks_out: &mut Vec<BlockOut>,
1129) {
1130 for block in block_def.iter_mut().rev() {
1133 if idx > block.range.start && idx < block.range.end {
1134 end_block(page, block);
1135 blocks_out.push(block.as_out());
1136
1137 block.range.start = idx;
1139 }
1140 }
1144}
1145
1146fn page_break(page: &mut Page) {
1148 page.page_no += 1;
1150
1151 let column = page.page_no % page.columns;
1152 let mirror = (page.page_no / page.columns) % 2 == 1;
1153
1154 page.page_start = (page.page_no / page.columns).saturating_mul(page.height);
1155 page.page_end = page
1156 .page_start
1157 .saturating_add(page.height.saturating_sub(page.bottom));
1158 page.x_pos = XPositions::new(page, column, mirror);
1159 page.y = page.page_start.saturating_add(page.top);
1160
1161 page.effective_line_spacing = 0;
1162 page.top_padding = 0;
1163 page.bottom_padding = 0;
1164 page.bottom_padding_break = 0;
1165}
1166
1167fn next_widget<W>(
1169 page: &mut Page,
1170 block_def: &mut VecDeque<BlockDef>,
1171 widget: &WidgetDef<W>,
1172 idx: usize,
1173 must_fit: bool,
1174) -> (Rect, Rect)
1175where
1176 W: Eq + Hash + Clone + Debug,
1177{
1178 page.y = page.y.saturating_add(page.effective_line_spacing);
1180
1181 page.effective_line_spacing = page.line_spacing;
1182 page.top_padding = 0;
1183 page.bottom_padding = 0;
1184 page.bottom_padding_break = 0;
1185
1186 for block in block_def.iter_mut() {
1188 if block.range.start == idx {
1189 start_block(page, block);
1190 }
1191 if block.range.start <= idx {
1192 widget_padding(page, idx, block);
1193 }
1194 }
1198
1199 let (label_area, widget_area, advance) = areas_and_advance(page, widget, must_fit);
1201
1202 page.y = page.y.saturating_add(advance);
1203
1204 (label_area, widget_area)
1205}
1206
1207fn start_block(page: &mut Page, block: &mut BlockDef) {
1209 block.area.x = page.x_pos.container_left;
1211 block.area.width = page
1212 .x_pos
1213 .container_right
1214 .saturating_sub(page.x_pos.container_left);
1215 block.area.y = page.y;
1216
1217 page.y = page.y.saturating_add(block.padding.top);
1219 page.top_padding += block.padding.top;
1220 page.x_pos.container_left = page.x_pos.container_left.saturating_add(block.padding.left);
1221 page.x_pos.container_right = page
1222 .x_pos
1223 .container_right
1224 .saturating_sub(block.padding.right);
1225}
1226
1227fn widget_padding(page: &mut Page, idx: usize, block: &mut BlockDef) {
1228 if block.range.end > idx + 1 {
1229 page.bottom_padding_break += block.padding.bottom;
1230 } else if block.range.end == idx + 1 {
1231 page.bottom_padding += block.padding.bottom;
1232 }
1233}
1234
1235fn next_blocks(
1236 page: &mut Page,
1237 block_def: &mut VecDeque<BlockDef>,
1238 idx: usize,
1239 blocks_out: &mut Vec<BlockOut>,
1240) {
1241 for block in block_def.iter_mut().rev() {
1244 if idx + 1 == block.range.end {
1245 end_block(page, block);
1246 blocks_out.push(block.as_out());
1247 }
1248 }
1252}
1253
1254fn push_blocks<W: Eq + Hash + Clone>(
1255 blocks_out: &mut Vec<BlockOut>,
1256 gen_layout: &mut GenericLayout<W>,
1257) {
1258 while let Some(cc) = blocks_out.pop() {
1259 gen_layout.add_block(cc.area, cc.block);
1260 }
1261}
1262
1263fn end_block(page: &mut Page, block: &mut BlockDef) {
1265 page.y = page.y.saturating_add(block.padding.bottom);
1267 page.x_pos.container_left = page.x_pos.container_left.saturating_sub(block.padding.left);
1268 page.x_pos.container_right = page
1269 .x_pos
1270 .container_right
1271 .saturating_add(block.padding.right);
1272
1273 block.area.height = page.y.saturating_sub(block.area.y);
1275}
1276
1277fn areas_and_advance<W: Debug + Clone>(
1280 page: &Page,
1281 widget: &WidgetDef<W>,
1282 must_fit: bool,
1283) -> (Rect, Rect, u16) {
1284 let stacked = matches!(
1289 widget.widget,
1290 FormWidget::Wide(_, _) | FormWidget::WideStretchX(_, _) | FormWidget::WideStretchXY(_, _)
1291 );
1292
1293 let mut label_height = match &widget.label {
1294 FormLabel::None => 0,
1295 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1296 FormLabel::Width(_) => 1,
1297 FormLabel::Size(_, h) => *h,
1298 };
1299
1300 let mut widget_height = match &widget.widget {
1301 FormWidget::None => 0,
1302 FormWidget::Width(_) => 1,
1303 FormWidget::Size(_, h) => *h,
1304 FormWidget::StretchY(_, h) => *h,
1305 FormWidget::Wide(_, h) => *h,
1306 FormWidget::StretchX(_, h) => *h,
1307 FormWidget::WideStretchX(_, h) => *h,
1308 FormWidget::StretchXY(_, h) => *h,
1309 FormWidget::WideStretchXY(_, h) => *h,
1310 };
1311
1312 let stretch_width = page
1313 .x_pos
1314 .widget_right
1315 .saturating_sub(page.x_pos.widget_left);
1316 let total_stretch_width = page
1317 .x_pos
1318 .widget_right
1319 .saturating_sub(page.x_pos.label_left);
1320
1321 let max_height = if !must_fit {
1322 page.height
1323 .saturating_sub(page.top)
1324 .saturating_sub(page.bottom)
1325 .saturating_sub(page.top_padding)
1326 .saturating_sub(page.bottom_padding)
1327 } else {
1328 page.height
1329 .saturating_sub(page.y - page.page_start)
1330 .saturating_sub(page.bottom)
1331 .saturating_sub(page.bottom_padding_break)
1332 };
1333
1334 if stacked {
1335 if label_height + widget_height > max_height {
1336 label_height = max(1, max_height.saturating_sub(widget_height));
1337 }
1338 if label_height + widget_height > max_height {
1339 widget_height = max(1, max_height.saturating_sub(label_height));
1340 }
1341 if label_height + widget_height > max_height {
1342 label_height = 0;
1343 }
1344 if label_height + widget_height > max_height {
1345 widget_height = max_height;
1346 }
1347
1348 let mut label_area = match &widget.label {
1349 FormLabel::None => Rect::new(
1350 page.x_pos.label_left, page.y,
1352 0,
1353 0,
1354 ),
1355 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1356 FormLabel::Width(_) => Rect::new(
1357 page.x_pos.label_left,
1358 page.y,
1359 page.x_pos.label_width,
1360 label_height,
1361 ),
1362 FormLabel::Size(_, _) => Rect::new(
1363 page.x_pos.label_left,
1364 page.y,
1365 page.x_pos.label_width,
1366 label_height,
1367 ),
1368 };
1369 match &widget.widget {
1370 FormWidget::Wide(_, _) => label_area.width = page.x_pos.total_width,
1371 FormWidget::WideStretchX(_, _) => label_area.width = total_stretch_width,
1372 FormWidget::WideStretchXY(_, _) => label_area.width = total_stretch_width,
1373 _ => {}
1374 }
1375
1376 let widget_area = match &widget.widget {
1377 FormWidget::None => unreachable!(),
1378 FormWidget::Width(_) => unreachable!(),
1379 FormWidget::Size(_, _) => unreachable!(),
1380 FormWidget::StretchY(_, _) => unreachable!(),
1381 FormWidget::Wide(_, _) => Rect::new(
1382 page.x_pos.label_left,
1383 page.y + label_height,
1384 page.x_pos.total_width,
1385 widget_height,
1386 ),
1387 FormWidget::StretchX(_, _) => unreachable!(),
1388 FormWidget::WideStretchX(_, _) => Rect::new(
1389 page.x_pos.label_left,
1390 page.y + label_height,
1391 total_stretch_width,
1392 widget_height,
1393 ),
1394 FormWidget::StretchXY(_, _) => unreachable!(),
1395 FormWidget::WideStretchXY(_, _) => Rect::new(
1396 page.x_pos.label_left,
1397 page.y + label_height,
1398 total_stretch_width,
1399 widget_height,
1400 ),
1401 };
1402
1403 (
1404 label_area,
1405 widget_area,
1406 label_area.height + widget_area.height,
1407 )
1408 } else {
1409 label_height = min(label_height, max_height);
1410 widget_height = min(widget_height, max_height);
1411 let height = max(label_height, widget_height);
1412
1413 let label_area = match &widget.label {
1414 FormLabel::None => Rect::new(
1415 page.x_pos.label_left, page.y,
1417 0,
1418 0,
1419 ),
1420 FormLabel::Str(_) | FormLabel::String(_) => unreachable!(),
1421 FormLabel::Width(_) => Rect::new(
1422 page.x_pos.label_left,
1423 page.y,
1424 page.x_pos.label_width,
1425 height,
1426 ),
1427 FormLabel::Size(_, _) => Rect::new(
1428 page.x_pos.label_left,
1429 page.y,
1430 page.x_pos.label_width,
1431 height,
1432 ),
1433 };
1434
1435 let widget_area = match &widget.widget {
1436 FormWidget::None => Rect::default(),
1437 FormWidget::Width(w) => Rect::new(
1438 page.x_pos.widget_left,
1439 page.y,
1440 min(*w, page.x_pos.widget_width),
1441 height,
1442 ),
1443 FormWidget::Size(w, _) => Rect::new(
1444 page.x_pos.widget_left,
1445 page.y,
1446 min(*w, page.x_pos.widget_width),
1447 height,
1448 ),
1449 FormWidget::StretchY(w, _) => Rect::new(
1450 page.x_pos.widget_left,
1451 page.y,
1452 min(*w, page.x_pos.widget_width),
1453 height,
1454 ),
1455 FormWidget::Wide(_, _) => unreachable!(),
1456 FormWidget::StretchX(_, _) => Rect::new(
1457 page.x_pos.widget_left, page.y,
1459 stretch_width,
1460 height,
1461 ),
1462 FormWidget::WideStretchX(_, _) => unreachable!(),
1463 FormWidget::StretchXY(_, _) => Rect::new(
1464 page.x_pos.widget_left, page.y,
1466 stretch_width,
1467 height,
1468 ),
1469 FormWidget::WideStretchXY(_, _) => unreachable!(),
1470 };
1471
1472 (
1473 label_area,
1474 widget_area,
1475 max(label_area.height, widget_area.height),
1476 )
1477 }
1478}
1479
1480fn adjust_y_stretch<W: Eq + Hash + Clone>(
1483 page: &Page,
1484 stretch_y: &mut Vec<usize>,
1485 gen_layout: &mut GenericLayout<W>,
1486) {
1487 let mut remainder = page.page_end.saturating_sub(page.y);
1488 if remainder == 0 {
1489 stretch_y.clear();
1490 return;
1491 }
1492
1493 let mut n = stretch_y.len() as u16;
1494 for y_idx in stretch_y.drain(..) {
1495 let stretch = remainder / n;
1498 remainder -= stretch;
1499 n -= 1;
1500
1501 let mut area = gen_layout.widget(y_idx);
1503 let test_y = area.bottom();
1504 let test_x = page.x_pos.container_left;
1505
1506 area.height += stretch;
1507 gen_layout.set_widget(y_idx, area);
1508
1509 for idx in y_idx + 1..gen_layout.widget_len() {
1511 let mut area = gen_layout.widget(idx);
1512 if area.y >= test_y {
1513 area.y += stretch;
1514 }
1515 gen_layout.set_widget(idx, area);
1516
1517 let mut area = gen_layout.label(idx);
1518 if area.y >= test_y {
1519 area.y += stretch;
1520 }
1521 gen_layout.set_label(idx, area);
1522 }
1523
1524 for idx in 0..gen_layout.block_len() {
1526 let mut area = gen_layout.block_area(idx);
1527 if area.x >= test_x && area.y >= test_y {
1528 area.y += stretch;
1529 }
1530 if area.x >= test_x && area.y <= test_y && area.bottom() > test_y {
1532 area.height += stretch;
1533 }
1534 gen_layout.set_block_area(idx, area);
1535 }
1536 }
1537}