tabled/settings/panel/
vertical_panel.rs1use crate::{
2 grid::config::{ColoredConfig, SpannedConfig},
3 grid::records::{ExactRecords, Records, RecordsMut, Resizable},
4 settings::TableOption,
5};
6
7#[derive(Debug)]
9pub struct VerticalPanel<S> {
10 text: S,
11 col: usize,
12}
13
14impl<S> VerticalPanel<S> {
15 pub fn new(col: usize, text: S) -> Self
17 where
18 S: AsRef<str>,
19 {
20 Self { text, col }
21 }
22
23 pub fn width(self, width: usize) -> VerticalPanel<String>
25 where
26 S: AsRef<str>,
27 {
28 let mut text = String::new();
29
30 if width > 0 {
31 text = split_string_by_width(self.text.as_ref(), width);
32 }
33
34 VerticalPanel {
35 text,
36 col: self.col,
37 }
38 }
39}
40
41impl<S, R, D> TableOption<R, ColoredConfig, D> for VerticalPanel<S>
42where
43 S: AsRef<str>,
44 R: Records + ExactRecords + Resizable + RecordsMut<String>,
45{
46 fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
47 let count_rows = records.count_rows();
48 let count_cols = records.count_columns();
49
50 if self.col > count_cols {
51 return;
52 }
53
54 let is_intersect_horizontal_span = (0..=records.count_rows())
55 .any(|row| cfg.is_cell_covered_by_column_span((row, self.col).into()));
56
57 if is_intersect_horizontal_span {
58 return;
59 }
60
61 move_columns_aside(records, self.col);
62 move_column_spans(cfg, self.col);
63
64 let text = self.text.as_ref().to_owned();
65 records.set((0, self.col).into(), text);
66
67 cfg.set_row_span((0, self.col).into(), count_rows);
68 }
69}
70
71fn move_columns_aside<R: Records + Resizable>(records: &mut R, column: usize) {
72 records.push_column();
73
74 let count_columns = records.count_columns();
75 let shift_count = count_columns - column;
76 for i in 1..shift_count {
77 let col = count_columns - i;
78 records.swap_column(col, col - 1);
79 }
80}
81
82fn move_column_spans(cfg: &mut SpannedConfig, target_column: usize) {
83 for (p, span) in cfg.get_column_spans() {
84 if p.col < target_column {
85 continue;
86 }
87
88 cfg.set_column_span(p, 1);
89 cfg.set_column_span(p + (0, 1), span);
90 }
91
92 for (p, span) in cfg.get_row_spans() {
93 if p.col < target_column {
94 continue;
95 }
96
97 cfg.set_row_span(p, 1);
98 cfg.set_row_span(p + (0, 1), span);
99 }
100}
101
102fn split_string_by_width(str: &str, width: usize) -> String {
103 if width == 0 {
104 return String::new();
105 }
106
107 let (lhs, rhs) = crate::util::string::split_str(str, width);
108 if rhs.is_empty() {
109 return lhs.into_owned();
110 }
111
112 let mut buf = lhs.into_owned();
113 let mut next = rhs.into_owned();
114 while !next.is_empty() {
115 let (lhs, rhs) = crate::util::string::split_str(&next, width);
116 buf.push('\n');
117 buf.push_str(&lhs);
118 next = rhs.into_owned();
119 }
120
121 buf
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_split_string_by_width() {
130 assert_eq!(split_string_by_width("123456789", 3), "123\n456\n789");
131 assert_eq!(split_string_by_width("123456789", 2), "12\n34\n56\n78\n9");
132 assert_eq!(
133 split_string_by_width("123456789", 1),
134 "1\n2\n3\n4\n5\n6\n7\n8\n9"
135 );
136 assert_eq!(split_string_by_width("123456789", 0), "");
137
138 assert_eq!(
139 split_string_by_width("\u{1b}[31;100m😳😳🏳️\u{1b}[39m\u{1b}[49m😳🏳️", 3),
140 {
141 #[cfg(feature = "ansi")]
142 {
143 "\u{1b}[31m\u{1b}[100m😳\u{1b}[39m\u{1b}[49m�\n\u{1b}[31m\u{1b}[100m🏳\u{fe0f}\u{1b}[39m\u{1b}[49m�\n🏳\u{fe0f}"
144 }
145 #[cfg(not(feature = "ansi"))]
146 {
147 "\u{1b}[3\n1;1\n00m\n😳�\n🏳\u{fe0f}\u{1b}\n[39\nm\u{1b}[\n49m\n😳🏳\n\u{fe0f}"
148 }
149 }
150 );
151 }
152}