1use colored::Colorize;
2use hex_color::HexColor;
3use crate::templates::*;
4use crate::constructs::*;
5
6
7#[derive(Debug)]
10pub struct Boxy {
11 pub type_enum: BoxType,
12 pub data : Vec<Vec<String>>,
13 pub sect_count: usize,
14 pub box_col : String,
15 pub colors : Vec<Vec<String>>,
16 pub int_padding: BoxPad,
17 pub ext_padding: BoxPad,
18 pub align : BoxAlign,
19 pub fixed_width: usize,
20 pub fixed_height: usize,
21 pub seg_v_div_count: Vec<usize>,
22 pub seg_v_div_ratio: Vec<Vec<usize>>,
23 pub tot_seg: usize,
24}
25
26impl Default for Boxy {
28 fn default() -> Self {
29 Self {
30 type_enum: BoxType::Single,
31 data : Vec::<Vec<String>>::new(),
32 sect_count: 0usize,
33 box_col: "#ffffff".to_string(),
34 colors : Vec::<Vec<String>>::new(),
35 int_padding: BoxPad::new(),
36 ext_padding: BoxPad::new(),
37 align : BoxAlign::Left,
38 fixed_width: 0usize,
39 fixed_height: 0usize,
40 seg_v_div_count: Vec::<usize>::new(),
41 seg_v_div_ratio: Vec::<Vec<usize>>::new(),
42 tot_seg: 0usize,
43 }
44 }
45}
46
47
48impl Boxy {
49 pub fn new(box_type: BoxType, box_color : &str) -> Self {
51 Boxy{
52 type_enum: box_type,
53 data : Vec::<Vec<String>>::new(),
54 sect_count: 0usize,
55 box_col : (&box_color).to_string(),
56 colors : Vec::<Vec<String>>::new(),
57 int_padding: BoxPad::new(),
58 ext_padding: BoxPad::new(),
59 align : BoxAlign::Left,
60 fixed_width: 0usize,
61 fixed_height: 0usize,
62 seg_v_div_count: Vec::<usize>::new(),
63 seg_v_div_ratio: Vec::<Vec<usize>>::new(),
64 tot_seg: 0usize,
65 }
66 }
67
68 pub fn add_text_sgmt(&mut self, data_string : &str, color : &str) {
73 self.data.push(vec![data_string.to_owned()]);
74 self.colors.push(vec![String::from(color)]);
75 self.sect_count+=1;
76 }
77
78 pub fn add_text_line_indx(&mut self, data_string : &str, seg_index : usize) {
81 self.data[seg_index].push(data_string.to_owned());
82 }
83
84 pub fn add_text_line(&mut self, data_string : &str) {
87 self.data[self.sect_count-1].push(String::from(data_string));
88 }
89
90 pub fn set_align(&mut self, align: BoxAlign) {
93 self.align = align;
94 }
95
96 pub fn set_int_padding(&mut self, int_padding : BoxPad) {
101 self.int_padding = int_padding;
102 }
103 pub fn set_ext_padding(&mut self, ext_padding : BoxPad) {
107 self.ext_padding = ext_padding;
108 }
109 pub fn set_padding(&mut self, ext_padding : BoxPad, int_padding : BoxPad) {
113 self.int_padding = int_padding;
114 self.ext_padding = ext_padding;
115 }
116
117 pub fn set_width(&mut self, width : usize) {
120 self.fixed_width = width;
121 }
122
123 pub fn set_height(&mut self, height : usize) {
128 self.fixed_height = height;
129 }
130
131 pub fn set_segment_ratios(&mut self, seg_index: usize, ratios: Vec<usize>) {
135 if seg_index >= self.seg_v_div_ratio.len() {
136 self.seg_v_div_ratio.resize(seg_index + 1, Vec::new());
137 }
138 self.seg_v_div_ratio[seg_index] = ratios;
139 }
140
141 pub fn display(&mut self) {
144 let disp_width = if self.fixed_width !=0 {
146 self.fixed_width
147 } else {
148 let size = termsize::get();
149 if size.is_some() {
150 size.unwrap().cols as usize - 20
151 } else {
152 return;
153 }
154 };
155
156 let col_truevals = HexColor::parse(&self.box_col).unwrap();
157 let box_pieces = map_box_type(&self.type_enum);
158 let horiz =box_pieces.horizontal.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b);
159
160 print!("{:>width$}", box_pieces.top_left.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width=self.ext_padding.left+1);
162 for _ in 0..(disp_width -2*self.ext_padding.right) {
163 print!("{}", horiz);
164 }
165 println!("{}", box_pieces.top_right.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
166
167 for i in 0..self.sect_count {
169 if i > 0 {
170 self.print_h_divider(&col_truevals, &disp_width);
171 }
172 self.display_segment(i, &disp_width);
173 }
174
175 print!("{:>width$}", box_pieces.bottom_left.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width=self.ext_padding.left+1);
177 for _ in 0..disp_width -2*self.ext_padding.right {
178 print!("{}", horiz);
179 }
180 println!("{}", box_pieces.bottom_right.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
181
182 }
183
184 fn display_segment(&mut self, seg_index: usize, disp_width: &usize) {
186
187 let col_truevals = HexColor::parse(&self.box_col).unwrap();
189
190 for i in 0..self.data[seg_index].len() {
192 let mut processed_data = String::with_capacity(self.data[seg_index][i].len()+1);
194 processed_data.push_str(self.data[seg_index][i].trim());
195 processed_data.push(' ');
196 let mut ws_indices = Vec::new();
197 let mut k = 0usize;
200 while k < processed_data.len() {
201 if processed_data.as_bytes()[k] == b' ' {
202 ws_indices.push(k);
203 }
204 k += 1;
205 }
206
207 let liner: Vec<String> = text_wrap_vec(&processed_data, &mut ws_indices, &disp_width.clone(), &self.ext_padding, &self.int_padding);
208
209 iter_line_prnt(&liner, map_box_type(&self.type_enum), &col_truevals, disp_width, &self.ext_padding, &self.int_padding, &self.align);
213
214 if i < self.data[seg_index].len() - 1 {
216 println!("{1:>width$}{}{1}", " ".repeat(disp_width - self.ext_padding.lr()),
217 map_box_type(&self.type_enum)
218 .vertical.to_string()
219 .truecolor(col_truevals.r, col_truevals.g, col_truevals.b),
220 width=self.ext_padding.left+1);
221 }
222 }
223 }
226
227 fn print_h_divider(&mut self, boxcol: &HexColor, disp_width: &usize){
229 let box_pieces = map_box_type(&self.type_enum);
230 let horiz = box_pieces.horizontal.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b);
231 print!("{:>width$}", box_pieces.left_t.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b), width=self.ext_padding.left+1);
232 for _ in 0..*disp_width-self.ext_padding.lr() {
233 print!("{}", horiz);
234 }
235 println!("{}", box_pieces.right_t.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b));
236 }
237
238 fn _display_segment_with_ratios(&mut self, seg_index: usize, terminal_size: &usize) {
250 let col_truevals = HexColor::parse(&self.box_col).unwrap();
251 let box_pieces = map_box_type(&self.type_enum);
252
253 let ratios = if seg_index < self.seg_v_div_ratio.len() {
255 &self.seg_v_div_ratio[seg_index]
256 } else {
257 static EMPTY_VEC: Vec<usize> = Vec::new();
258 &EMPTY_VEC
259 };
260
261 if ratios.is_empty() {
262 self.display_segment(seg_index, terminal_size);
264 return;
265 }
266
267 let total_ratio: usize = ratios.iter().sum();
269 let printable_width = terminal_size - self.ext_padding.lr();
270 let segment_widths: Vec<usize> = ratios.iter().map(|r| r * printable_width / total_ratio).collect();
271
272 let mut mini_segments: Vec<Vec<String>> = vec![Vec::new(); ratios.len()];
274 let lines = &self.data[seg_index];
275 for line in lines.iter() {
276 let mut processed_data = String::with_capacity(line.len() + 1);
277 processed_data.push_str(line.trim());
278 processed_data.push(' ');
279
280 let mut ws_indices = Vec::new();
281 let mut k = 0usize;
282 while k < processed_data.len() {
283 if processed_data.as_bytes()[k] == b' ' {
284 ws_indices.push(k);
285 }
286 k += 1;
287 }
288
289 for (j, width) in segment_widths.iter().enumerate() {
291 let liner = text_wrap_vec(&processed_data, &mut ws_indices, width, &self.ext_padding, &self.int_padding);
292 mini_segments[j].extend(liner);
293 }
294 }
295
296 let max_lines = mini_segments.iter().map(|seg| seg.len()).max().unwrap_or(0);
298 for line_index in 0..max_lines {
299 print!("{:>width$}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width = self.ext_padding.left + 1);
301
302 for (j, segment) in mini_segments.iter().enumerate() {
303 if line_index < segment.len() {
304 print!("{:<pad$}", " ", pad = self.int_padding.left);
306 print!("{:<width$}", segment[line_index], width = segment_widths[j] - self.int_padding.lr());
307 print!("{:<pad$}", " ", pad = self.int_padding.right);
308 } else {
309 print!("{:<width$}", " ", width = segment_widths[j]);
311 }
312
313 if j < mini_segments.len() - 1 {
315 print!("{}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
316 }
317 }
318
319 println!("{}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
321 }
322 }
323}
324
325fn nearest_whitespace(map: &mut Vec<usize>, printable_length: &usize, start_index: usize) -> usize {
328 let mut next_ws = 0;
329 for i in map {
330 if *i > start_index && *i-start_index <= *printable_length {
331 next_ws = *i;
332 }
333 }
334 if next_ws == 0 {
336 next_ws = start_index + printable_length;
337 }
338 next_ws
339}
340
341fn text_wrap_vec(data:&str, map: &mut Vec<usize>, disp_width: &usize, ext_padding: &BoxPad, int_padding: &BoxPad) -> Vec<String> {
346 let mut liner: Vec<String> = Vec::new();
347 let mut start_index = 0;
348
349 while start_index < data.len() {
350 let next_ws = nearest_whitespace(map, &(disp_width - (int_padding.lr() + ext_padding.lr()) - 2), start_index);
351 liner.push(data[start_index..next_ws].to_string());
352 if next_ws >= data.len()-1 {break;}
353 start_index = next_ws+1;
354 }
355 liner
356
357 }
366
367
368fn iter_line_prnt(liner : &[String], box_pieces:BoxTemplates, box_col: &HexColor, disp_width: &usize, ext_padding: &BoxPad, int_padding: &BoxPad, align: &BoxAlign) {
369 let printable_area = disp_width - (int_padding.lr() + ext_padding.lr());
370 let vertical = box_pieces.vertical.to_string().truecolor(box_col.r, box_col.g, box_col.b);
371 match align {
372 BoxAlign::Left => {
373 for i in liner.iter() {
374 print!("{:>width$}", vertical, width=ext_padding.left+1);
375 print!("{:<pad$}", " ", pad=int_padding.left);
376 print!("{:<width$}", i, width=printable_area-2); print!("{:<pad$}", " ", pad=int_padding.right);
378 println!("{}", vertical);
379 }
380 },
381 BoxAlign::Center => {
382 for i in liner.iter() {
383 print!("{:>width$}", vertical, width=ext_padding.left+1);
384 print!("{:<pad$}", " ", pad=int_padding.left+((printable_area-i.len())/2));
385 print!("{}", i);
386 print!("{:<pad$}", " ", pad=int_padding.right+(printable_area-i.len())-((printable_area-i.len())/2));
387 println!("{}", vertical);
388 }
389 },
390 BoxAlign::Right => {
391 for i in liner.iter() {
392 print!("{:>width$}", vertical, width=ext_padding.left+1);
393 print!("{:<pad$}", " ", pad=int_padding.left);
394 print!("{:>width$}", i, width=printable_area-2); print!("{:<pad$}", " ", pad=int_padding.right);
396 println!("{}", vertical);
397 }
398 }
399 }
400}
401
402fn map_box_type (boxtype : &BoxType) -> BoxTemplates{
404 match boxtype {
405 BoxType::Classic => CLASSIC_TEMPLATE,
406 BoxType::Single => SINGLE_TEMPLATE,
407 BoxType::DoubleHorizontal => DOUB_H_TEMPLATE,
408 BoxType::DoubleVertical => DOUB_V_TEMPLATE,
409 BoxType::Double => DOUBLE_TEMPLATE,
410 BoxType::Bold => BOLD_TEMPLATE,
411 BoxType::Rounded => ROUNDED_TEMPLATE,
412 BoxType::BoldCorners => BOLD_CORNERS_TEMPLATE,
413 }
414}
415
416pub fn resolve_col(dat : String) -> String {
421 dat
422}
423pub fn resolve_pad(dat : String) -> BoxPad {
425 BoxPad::uniform(dat.parse::<usize>().unwrap_or(0usize))
426}
427pub fn resolve_align(dat : String) -> BoxAlign {
429 match &*dat {
430 "center" => BoxAlign::Center,
431 "right" => BoxAlign::Right,
432 "left" => BoxAlign::Left,
433 _ => BoxAlign::Left,
434 }
435}
436pub fn resolve_type(dat : String) -> BoxType{
438 match &*dat {
439 "classic" => BoxType::Classic,
440 "single" => BoxType::Single,
441 "double_horizontal" => BoxType::DoubleHorizontal,
442 "double_vertical" => BoxType::DoubleVertical,
443 "double" => BoxType::Double,
444 "bold" => BoxType::Bold,
445 "rounded" => BoxType::Rounded,
446 "bold_corners" => BoxType::BoldCorners,
447 _ => BoxType::Single,
448 }
449}
450pub fn resolve_segments(dat : String) -> usize {
452 dat.parse().expect("failed to parse total segment number")
453}