1 | use crate::{ |
2 | grid::config::{ColoredConfig, SpannedConfig}, |
3 | grid::records::{ExactRecords, Records, RecordsMut, Resizable}, |
4 | settings::TableOption, |
5 | }; |
6 | |
7 | /// A vertical/row span from 0 to a count columns. |
8 | #[derive (Debug)] |
9 | pub struct VerticalPanel<S> { |
10 | text: S, |
11 | col: usize, |
12 | } |
13 | |
14 | impl<S> VerticalPanel<S> { |
15 | /// Creates a new vertical panel. |
16 | pub fn new(col: usize, text: S) -> Self |
17 | where |
18 | S: AsRef<str>, |
19 | { |
20 | Self { text, col } |
21 | } |
22 | |
23 | /// Split the set text to a certain width, so it fits within it. |
24 | 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 | |
41 | impl<S, R, D> TableOption<R, ColoredConfig, D> for VerticalPanel<S> |
42 | where |
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))); |
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), text); |
66 | |
67 | cfg.set_row_span((0, self.col), count_rows); |
68 | } |
69 | } |
70 | |
71 | fn move_columns_aside<R: Records + Resizable>(records: &mut R, column: usize) { |
72 | records.push_column(); |
73 | |
74 | let count_columns: usize = records.count_columns(); |
75 | let shift_count: usize = count_columns - column; |
76 | for i in 1..shift_count { |
77 | let col: usize = count_columns - i; |
78 | records.swap_column(lhs:col, rhs:col - 1); |
79 | } |
80 | } |
81 | |
82 | fn move_column_spans(cfg: &mut SpannedConfig, target_column: usize) { |
83 | for ((row: usize, col: usize), span: usize) in cfg.get_column_spans() { |
84 | if col < target_column { |
85 | continue; |
86 | } |
87 | |
88 | cfg.set_column_span((row, col), span:1); |
89 | cfg.set_column_span((row, col + 1), span); |
90 | } |
91 | |
92 | for ((row: usize, col: usize), span: usize) in cfg.get_row_spans() { |
93 | if col < target_column { |
94 | continue; |
95 | } |
96 | |
97 | cfg.set_row_span((row, col), span:1); |
98 | cfg.set_row_span((row, col + 1), span); |
99 | } |
100 | } |
101 | |
102 | fn split_string_by_width(str: &str, width: usize) -> String { |
103 | if width == 0 { |
104 | return String::new(); |
105 | } |
106 | |
107 | let (lhs: Cow<'_, str>, rhs: Cow<'_, str>) = crate::util::string::split_str(s:str, width); |
108 | if rhs.is_empty() { |
109 | return lhs.into_owned(); |
110 | } |
111 | |
112 | let mut buf: String = lhs.into_owned(); |
113 | let mut next: String = rhs.into_owned(); |
114 | while !next.is_empty() { |
115 | let (lhs: Cow<'_, str>, rhs: Cow<'_, str>) = crate::util::string::split_str(&next, width); |
116 | buf.push(ch:' \n' ); |
117 | buf.push_str(&lhs); |
118 | next = rhs.into_owned(); |
119 | } |
120 | |
121 | buf |
122 | } |
123 | |
124 | #[cfg (test)] |
125 | mod 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}[31 \n;10 \n0m� \n😳🏳 \n\u{fe0f}\u{1b}[39 \nm \u{1b}[4 \n9m� \n🏳 \u{fe0f}" |
148 | } |
149 | } |
150 | ); |
151 | } |
152 | } |
153 | |