1use colored::Colorize;
2use hex_color::HexColor;
3use crate::templates::*;
4use crate::constructs::*;
5
6
7#[derive(Debug)]
12pub struct Boxy {
13 pub type_enum: BoxType,
14 pub data : Vec<Vec<String>>,
15 pub sect_count: usize,
16 pub box_col : String,
17 pub colors : Vec<Vec<String>>,
18 pub int_padding: BoxPad,
19 pub ext_padding: BoxPad,
20 pub align : BoxAlign,
21 pub fixed_width: usize,
22 pub fixed_height: usize,
23 pub seg_v_div_count: Vec<usize>,
24 pub seg_v_div_ratio: Vec<Vec<usize>>,
25 pub tot_seg: usize,
26}
27
28impl Default for Boxy {
30 fn default() -> Self {
31 Self {
32 type_enum: BoxType::Single,
33 data : Vec::<Vec<String>>::new(),
34 sect_count: 0usize,
35 box_col: "#ffffff".to_string(),
36 colors : Vec::<Vec<String>>::new(),
37 int_padding: BoxPad::new(),
38 ext_padding: BoxPad::new(),
39 align : BoxAlign::Left,
40 fixed_width: 0usize,
41 fixed_height: 0usize,
42 seg_v_div_count: Vec::<usize>::new(),
43 seg_v_div_ratio: Vec::<Vec<usize>>::new(),
44 tot_seg: 0usize,
45 }
46 }
47}
48
49
50impl Boxy {
51 pub fn new(box_type: BoxType, box_color : &str) -> Self {
53 Boxy{
54 type_enum: box_type,
55 data : Vec::<Vec<String>>::new(),
56 sect_count: 0usize,
57 box_col : (&box_color).to_string(),
58 colors : Vec::<Vec<String>>::new(),
59 int_padding: BoxPad::new(),
60 ext_padding: BoxPad::new(),
61 align : BoxAlign::Left,
62 fixed_width: 0usize,
63 fixed_height: 0usize,
64 seg_v_div_count: Vec::<usize>::new(),
65 seg_v_div_ratio: Vec::<Vec<usize>>::new(),
66 tot_seg: 0usize,
67 }
68 }
69
70 pub fn add_text_sgmt(&mut self, data_string : &str, color : &str) {
75 self.data.push(vec![data_string.to_owned()]);
76 self.colors.push(vec![String::from(color)]);
77 self.sect_count+=1;
78 }
79
80 pub fn add_text_line_indx(&mut self, data_string : &str, seg_index : usize) {
83 self.data[seg_index].push(data_string.to_owned());
84 }
85
86 pub fn add_text_line(&mut self, data_string : &str) {
89 self.data[self.sect_count-1].push(String::from(data_string));
90 }
91
92 pub fn set_align(&mut self, align: BoxAlign) {
95 self.align = align;
96 }
97
98 pub fn set_int_padding(&mut self, int_padding : BoxPad) {
103 self.int_padding = int_padding;
104 }
105 pub fn set_ext_padding(&mut self, ext_padding : BoxPad) {
109 self.ext_padding = ext_padding;
110 }
111 pub fn set_padding(&mut self, ext_padding : BoxPad, int_padding : BoxPad) {
115 self.int_padding = int_padding;
116 self.ext_padding = ext_padding;
117 }
118
119 pub fn set_width(&mut self, width : usize) {
122 self.fixed_width = width;
123 }
124
125 pub fn set_height(&mut self, height : usize) {
130 self.fixed_height = height;
131 }
132
133 pub fn set_segment_ratios(&mut self, seg_index: usize, ratios: Vec<usize>) {
137 if seg_index >= self.seg_v_div_ratio.len() {
138 self.seg_v_div_ratio.resize(seg_index + 1, Vec::new());
139 }
140 self.seg_v_div_ratio[seg_index] = ratios;
141 }
142
143 pub fn display(&mut self) {
146 let disp_width = if self.fixed_width !=0 {
148 self.fixed_width
149 } else {
150 let size = termsize::get();
151 if size.is_some() {
152 size.unwrap().cols as usize - 20
153 } else {
154 return;
155 }
156 };
157
158 let col_truevals = HexColor::parse(&self.box_col).unwrap();
159 let box_pieces = map_box_type(&self.type_enum);
160 let horiz =box_pieces.horizontal.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b);
161
162 print!("{:>width$}", box_pieces.top_left.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width=self.ext_padding.left+1);
164 for _ in 0..(disp_width -2*self.ext_padding.right) {
165 print!("{}", horiz);
166 }
167 println!("{}", box_pieces.top_right.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
168
169 for i in 0..self.sect_count {
171 if i > 0 {
172 self.print_h_divider(&col_truevals, &disp_width);
173 }
174 self.display_segment(i, &disp_width);
175 }
176
177 print!("{:>width$}", box_pieces.bottom_left.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width=self.ext_padding.left+1);
179 for _ in 0..disp_width -2*self.ext_padding.right {
180 print!("{}", horiz);
181 }
182 println!("{}", box_pieces.bottom_right.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
183
184 }
185
186 fn display_segment(&mut self, seg_index: usize, disp_width: &usize) {
188
189 let col_truevals = HexColor::parse(&self.box_col).unwrap();
191
192 for i in 0..self.data[seg_index].len() {
194 let mut processed_data = String::with_capacity(self.data[seg_index][i].len()+1);
196 processed_data.push_str(self.data[seg_index][i].trim());
197 processed_data.push(' ');
198 let mut ws_indices = Vec::new();
199 let mut k = 0usize;
202 while k < processed_data.len() {
203 if processed_data.as_bytes()[k] == b' ' {
204 ws_indices.push(k);
205 }
206 k += 1;
207 }
208
209 let liner: Vec<String> = text_wrap_vec(&processed_data, &mut ws_indices, &disp_width.clone(), &self.ext_padding, &self.int_padding);
210
211 iter_line_prnt(&liner, map_box_type(&self.type_enum), &col_truevals, disp_width, &self.ext_padding, &self.int_padding, &self.align);
215
216 if i < self.data[seg_index].len() - 1 {
218 println!("{1:>width$}{}{1}", " ".repeat(disp_width - self.ext_padding.lr()),
219 map_box_type(&self.type_enum)
220 .vertical.to_string()
221 .truecolor(col_truevals.r, col_truevals.g, col_truevals.b),
222 width=self.ext_padding.left+1);
223 }
224 }
225 }
228
229 fn print_h_divider(&mut self, boxcol: &HexColor, disp_width: &usize){
231 let box_pieces = map_box_type(&self.type_enum);
232 let horiz = box_pieces.horizontal.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b);
233 print!("{:>width$}", box_pieces.left_t.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b), width=self.ext_padding.left+1);
234 for _ in 0..*disp_width-self.ext_padding.lr() {
235 print!("{}", horiz);
236 }
237 println!("{}", box_pieces.right_t.to_string().truecolor(boxcol.r, boxcol.g, boxcol.b));
238 }
239
240 fn _display_segment_with_ratios(&mut self, seg_index: usize, terminal_size: &usize) {
252 let col_truevals = HexColor::parse(&self.box_col).unwrap();
253 let box_pieces = map_box_type(&self.type_enum);
254
255 let ratios = if seg_index < self.seg_v_div_ratio.len() {
257 &self.seg_v_div_ratio[seg_index]
258 } else {
259 static EMPTY_VEC: Vec<usize> = Vec::new();
260 &EMPTY_VEC
261 };
262
263 if ratios.is_empty() {
264 self.display_segment(seg_index, terminal_size);
266 return;
267 }
268
269 let total_ratio: usize = ratios.iter().sum();
271 let printable_width = terminal_size - self.ext_padding.lr();
272 let segment_widths: Vec<usize> = ratios.iter().map(|r| r * printable_width / total_ratio).collect();
273
274 let mut mini_segments: Vec<Vec<String>> = vec![Vec::new(); ratios.len()];
276 let lines = &self.data[seg_index];
277 for line in lines.iter() {
278 let mut processed_data = String::with_capacity(line.len() + 1);
279 processed_data.push_str(line.trim());
280 processed_data.push(' ');
281
282 let mut ws_indices = Vec::new();
283 let mut k = 0usize;
284 while k < processed_data.len() {
285 if processed_data.as_bytes()[k] == b' ' {
286 ws_indices.push(k);
287 }
288 k += 1;
289 }
290
291 for (j, width) in segment_widths.iter().enumerate() {
293 let liner = text_wrap_vec(&processed_data, &mut ws_indices, width, &self.ext_padding, &self.int_padding);
294 mini_segments[j].extend(liner);
295 }
296 }
297
298 let max_lines = mini_segments.iter().map(|seg| seg.len()).max().unwrap_or(0);
300 for line_index in 0..max_lines {
301 print!("{:>width$}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b), width = self.ext_padding.left + 1);
303
304 for (j, segment) in mini_segments.iter().enumerate() {
305 if line_index < segment.len() {
306 print!("{:<pad$}", " ", pad = self.int_padding.left);
308 print!("{:<width$}", segment[line_index], width = segment_widths[j] - self.int_padding.lr());
309 print!("{:<pad$}", " ", pad = self.int_padding.right);
310 } else {
311 print!("{:<width$}", " ", width = segment_widths[j]);
313 }
314
315 if j < mini_segments.len() - 1 {
317 print!("{}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
318 }
319 }
320
321 println!("{}", box_pieces.vertical.to_string().truecolor(col_truevals.r, col_truevals.g, col_truevals.b));
323 }
324 }
325}
326
327fn nearest_whitespace(map: &mut Vec<usize>, printable_length: &usize, start_index: usize) -> usize {
330 let mut next_ws = 0;
331 for i in map {
332 if *i > start_index && *i-start_index <= *printable_length {
333 next_ws = *i;
334 }
335 }
336 if next_ws == 0 {
338 next_ws = start_index + printable_length;
339 }
340 next_ws
341}
342
343fn text_wrap_vec(data:&str, map: &mut Vec<usize>, disp_width: &usize, ext_padding: &BoxPad, int_padding: &BoxPad) -> Vec<String> {
348 let mut liner: Vec<String> = Vec::new();
349 let mut start_index = 0;
350
351 while start_index < data.len() {
352 let next_ws = nearest_whitespace(map, &(disp_width - (int_padding.lr() + ext_padding.lr()) - 2), start_index);
353 liner.push(data[start_index..next_ws].to_string());
354 if next_ws >= data.len()-1 {break;}
355 start_index = next_ws+1;
356 }
357 liner
358
359 }
368
369
370fn iter_line_prnt(liner : &[String], box_pieces:BoxTemplates, box_col: &HexColor, disp_width: &usize, ext_padding: &BoxPad, int_padding: &BoxPad, align: &BoxAlign) {
371 let printable_area = disp_width - (int_padding.lr() + ext_padding.lr());
372 let vertical = box_pieces.vertical.to_string().truecolor(box_col.r, box_col.g, box_col.b);
373 match align {
374 BoxAlign::Left => {
375 for i in liner.iter() {
376 print!("{:>width$}", vertical, width=ext_padding.left+1);
377 print!("{:<pad$}", " ", pad=int_padding.left);
378 print!("{:<width$}", i, width=printable_area-2); print!("{:<pad$}", " ", pad=int_padding.right);
380 println!("{}", vertical);
381 }
382 },
383 BoxAlign::Center => {
384 for i in liner.iter() {
385 print!("{:>width$}", vertical, width=ext_padding.left+1);
386 print!("{:<pad$}", " ", pad=int_padding.left+((printable_area-i.len())/2));
387 print!("{}", i);
388 print!("{:<pad$}", " ", pad=int_padding.right+(printable_area-i.len())-((printable_area-i.len())/2));
389 println!("{}", vertical);
390 }
391 },
392 BoxAlign::Right => {
393 for i in liner.iter() {
394 print!("{:>width$}", vertical, width=ext_padding.left+1);
395 print!("{:<pad$}", " ", pad=int_padding.left);
396 print!("{:>width$}", i, width=printable_area-2); print!("{:<pad$}", " ", pad=int_padding.right);
398 println!("{}", vertical);
399 }
400 }
401 }
402}
403
404fn map_box_type (boxtype : &BoxType) -> BoxTemplates{
406 match boxtype {
407 BoxType::Classic => CLASSIC_TEMPLATE,
408 BoxType::Single => SINGLE_TEMPLATE,
409 BoxType::DoubleHorizontal => DOUB_H_TEMPLATE,
410 BoxType::DoubleVertical => DOUB_V_TEMPLATE,
411 BoxType::Double => DOUBLE_TEMPLATE,
412 BoxType::Bold => BOLD_TEMPLATE,
413 BoxType::Rounded => ROUNDED_TEMPLATE,
414 BoxType::BoldCorners => BOLD_CORNERS_TEMPLATE,
415 }
416}
417
418pub fn resolve_col(dat : String) -> String {
423 dat
424}
425pub fn resolve_pad(dat : String) -> BoxPad {
427 BoxPad::uniform(dat.parse::<usize>().unwrap_or(0usize))
428}
429pub fn resolve_align(dat : String) -> BoxAlign {
431 match &*dat {
432 "center" => BoxAlign::Center,
433 "right" => BoxAlign::Right,
434 "left" => BoxAlign::Left,
435 _ => BoxAlign::Left,
436 }
437}
438pub fn resolve_type(dat : String) -> BoxType{
440 match &*dat {
441 "classic" => BoxType::Classic,
442 "single" => BoxType::Single,
443 "double_horizontal" => BoxType::DoubleHorizontal,
444 "double_vertical" => BoxType::DoubleVertical,
445 "double" => BoxType::Double,
446 "bold" => BoxType::Bold,
447 "rounded" => BoxType::Rounded,
448 "bold_corners" => BoxType::BoldCorners,
449 _ => BoxType::Single,
450 }
451}
452pub fn resolve_segments(dat : String) -> usize {
454 dat.parse().expect("failed to parse total segment number")
455}