| 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 | |