1 | //! A module which contains configuration options for a [`Grid`]. |
2 | //! |
3 | //! [`Grid`]: crate::grid::iterable::Grid |
4 | |
5 | mod borders_config; |
6 | mod entity_map; |
7 | mod offset; |
8 | |
9 | use std::collections::HashMap; |
10 | |
11 | use crate::ansi::{ANSIBuf, ANSIStr}; |
12 | use crate::config::compact::CompactConfig; |
13 | use crate::config::Formatting; |
14 | use crate::config::{ |
15 | AlignmentHorizontal, AlignmentVertical, Border, Borders, Entity, Indent, Position, Sides, |
16 | }; |
17 | use borders_config::BordersConfig; |
18 | |
19 | pub use self::{entity_map::EntityMap, offset::Offset}; |
20 | |
21 | /// HorizontalLine represents a horizontal border line. |
22 | type HorizontalLine = super::HorizontalLine<char>; |
23 | |
24 | /// VerticalLine represents a vertical border line. |
25 | type VerticalLine = super::VerticalLine<char>; |
26 | |
27 | /// This structure represents a settings of a grid. |
28 | /// |
29 | /// grid: crate::Grid. |
30 | #[derive (Debug, PartialEq, Eq, Clone)] |
31 | pub struct SpannedConfig { |
32 | margin: Sides<ColoredMarginIndent>, |
33 | padding: EntityMap<Sides<ColoredIndent>>, |
34 | alignment_h: EntityMap<AlignmentHorizontal>, |
35 | alignment_v: EntityMap<AlignmentVertical>, |
36 | formatting: EntityMap<Formatting>, |
37 | span_columns: HashMap<Position, usize>, |
38 | span_rows: HashMap<Position, usize>, |
39 | borders: BordersConfig<char>, |
40 | borders_colors: BordersConfig<ANSIBuf>, |
41 | borders_missing_char: char, |
42 | horizontal_chars: HashMap<Position, HashMap<Offset, char>>, |
43 | horizontal_colors: HashMap<Position, HashMap<Offset, ANSIBuf>>, // squash a map to be HashMap<(Pos, Offset), char> |
44 | vertical_chars: HashMap<Position, HashMap<Offset, char>>, |
45 | vertical_colors: HashMap<Position, HashMap<Offset, ANSIBuf>>, |
46 | justification: EntityMap<char>, |
47 | justification_color: EntityMap<Option<ANSIBuf>>, |
48 | } |
49 | |
50 | impl Default for SpannedConfig { |
51 | fn default() -> Self { |
52 | Self { |
53 | margin: Sides::default(), |
54 | padding: EntityMap::default(), |
55 | formatting: EntityMap::default(), |
56 | alignment_h: EntityMap::new(global:AlignmentHorizontal::Left), |
57 | alignment_v: EntityMap::new(global:AlignmentVertical::Top), |
58 | span_columns: HashMap::default(), |
59 | span_rows: HashMap::default(), |
60 | borders: BordersConfig::default(), |
61 | borders_colors: BordersConfig::default(), |
62 | borders_missing_char: ' ' , |
63 | horizontal_chars: HashMap::default(), |
64 | horizontal_colors: HashMap::default(), |
65 | vertical_chars: HashMap::default(), |
66 | vertical_colors: HashMap::default(), |
67 | justification: EntityMap::new(global:' ' ), |
68 | justification_color: EntityMap::default(), |
69 | } |
70 | } |
71 | } |
72 | |
73 | impl SpannedConfig { |
74 | /// Set a margin of a grid. |
75 | pub fn set_margin(&mut self, margin: Sides<Indent>) { |
76 | self.margin.left.indent = margin.left; |
77 | self.margin.right.indent = margin.right; |
78 | self.margin.top.indent = margin.top; |
79 | self.margin.bottom.indent = margin.bottom; |
80 | } |
81 | |
82 | /// Set a color of margin of a grid. |
83 | pub fn set_margin_color(&mut self, margin: Sides<Option<ANSIBuf>>) { |
84 | self.margin.left.color = margin.left; |
85 | self.margin.right.color = margin.right; |
86 | self.margin.top.color = margin.top; |
87 | self.margin.bottom.color = margin.bottom; |
88 | } |
89 | |
90 | /// Set an offset of margin of a grid. |
91 | pub fn set_margin_offset(&mut self, margin: Sides<Offset>) { |
92 | self.margin.left.offset = margin.left; |
93 | self.margin.right.offset = margin.right; |
94 | self.margin.top.offset = margin.top; |
95 | self.margin.bottom.offset = margin.bottom; |
96 | } |
97 | |
98 | /// Returns a margin value currently set. |
99 | pub fn get_margin(&self) -> Sides<Indent> { |
100 | Sides::new( |
101 | self.margin.left.indent, |
102 | self.margin.right.indent, |
103 | self.margin.top.indent, |
104 | self.margin.bottom.indent, |
105 | ) |
106 | } |
107 | |
108 | /// Returns a margin color value currently set. |
109 | pub fn get_margin_color(&self) -> Sides<Option<ANSIBuf>> { |
110 | Sides::new( |
111 | self.margin.left.color.clone(), |
112 | self.margin.right.color.clone(), |
113 | self.margin.top.color.clone(), |
114 | self.margin.bottom.color.clone(), |
115 | ) |
116 | } |
117 | |
118 | /// Returns a margin offset value currently set. |
119 | pub fn get_margin_offset(&self) -> Sides<Offset> { |
120 | Sides::new( |
121 | self.margin.left.offset, |
122 | self.margin.right.offset, |
123 | self.margin.top.offset, |
124 | self.margin.bottom.offset, |
125 | ) |
126 | } |
127 | |
128 | /// Removes border changes. |
129 | pub fn remove_borders(&mut self) { |
130 | self.borders = BordersConfig::default(); |
131 | } |
132 | |
133 | /// Removes border changes. |
134 | pub fn remove_borders_colors(&mut self) { |
135 | self.borders_colors = BordersConfig::default(); |
136 | } |
137 | |
138 | /// Removes border changes. |
139 | pub fn remove_color_line_horizontal(&mut self) { |
140 | self.horizontal_colors.clear(); |
141 | } |
142 | |
143 | /// Removes border changes. |
144 | pub fn remove_color_line_vertical(&mut self) { |
145 | self.vertical_colors.clear(); |
146 | } |
147 | |
148 | /// Removes border changes. |
149 | pub fn remove_horizontal_chars(&mut self) { |
150 | self.horizontal_chars.clear(); |
151 | } |
152 | |
153 | /// Removes border changes. |
154 | pub fn remove_vertical_chars(&mut self) { |
155 | self.vertical_chars.clear(); |
156 | } |
157 | |
158 | /// Set the [`Borders`] value as correct one. |
159 | pub fn set_borders(&mut self, borders: Borders<char>) { |
160 | self.borders.set_borders(borders); |
161 | } |
162 | |
163 | /// Gets a global border value if set. |
164 | pub fn get_border_default(&self) -> Option<&char> { |
165 | self.borders.get_global() |
166 | } |
167 | |
168 | /// Set the all [`Borders`] values to a char. |
169 | pub fn set_border_default(&mut self, c: char) { |
170 | self.borders.set_global(c); |
171 | } |
172 | |
173 | /// Returns a current [`Borders`] structure. |
174 | pub fn get_borders(&self) -> &Borders<char> { |
175 | self.borders.get_borders() |
176 | } |
177 | |
178 | /// Set the border line by row index. |
179 | /// |
180 | /// Row `0` means the top row. |
181 | /// Row `grid.count_rows()` means the bottom row. |
182 | pub fn insert_horizontal_line(&mut self, line: usize, val: HorizontalLine) { |
183 | self.borders.insert_horizontal_line(line, val); |
184 | } |
185 | |
186 | /// Sets off the border line by row index if any were set |
187 | /// |
188 | /// Row `0` means the top row. |
189 | /// Row `grid.count_rows()` means the bottom row. |
190 | pub fn remove_horizontal_line(&mut self, line: usize, count_rows: usize) { |
191 | self.borders.remove_horizontal_line(line, count_rows); |
192 | } |
193 | |
194 | /// Gets a overridden vertical line. |
195 | /// |
196 | /// Row `0` means the left row. |
197 | /// Row `grid.count_columns()` means the right most row. |
198 | pub fn get_vertical_line(&self, line: usize) -> Option<&VerticalLine> { |
199 | self.borders.get_vertical_line(line) |
200 | } |
201 | |
202 | /// Gets all overridden vertical lines. |
203 | /// |
204 | /// Row `0` means the top row. |
205 | /// Row `grid.count_rows()` means the bottom row. |
206 | pub fn get_vertical_lines(&self) -> HashMap<usize, VerticalLine> { |
207 | self.borders.get_vertical_lines() |
208 | } |
209 | |
210 | /// Set the border line by column index. |
211 | /// |
212 | /// Row `0` means the left row. |
213 | /// Row `grid.count_columns()` means the right most row. |
214 | pub fn insert_vertical_line(&mut self, line: usize, val: VerticalLine) { |
215 | self.borders.insert_vertical_line(line, val); |
216 | } |
217 | |
218 | /// Sets off the border line by column index if any were set |
219 | /// |
220 | /// Row `0` means the left row. |
221 | /// Row `grid.count_columns()` means the right most row. |
222 | pub fn remove_vertical_line(&mut self, line: usize, count_columns: usize) { |
223 | self.borders.remove_vertical_line(line, count_columns); |
224 | } |
225 | |
226 | /// Gets a overridden line. |
227 | /// |
228 | /// Row `0` means the top row. |
229 | /// Row `grid.count_rows()` means the bottom row. |
230 | pub fn get_horizontal_line(&self, line: usize) -> Option<&HorizontalLine> { |
231 | self.borders.get_horizontal_line(line) |
232 | } |
233 | |
234 | /// Gets all overridden lines. |
235 | /// |
236 | /// Row `0` means the top row. |
237 | /// Row `grid.count_rows()` means the bottom row. |
238 | pub fn get_horizontal_lines(&self) -> HashMap<usize, HorizontalLine> { |
239 | self.borders.get_horizontal_lines() |
240 | } |
241 | |
242 | /// Override a character on a horizontal line. |
243 | /// |
244 | /// If borders are not set the char won't be used. |
245 | /// |
246 | /// It takes not cell position but line as row and column of a cell; |
247 | /// So its range is line <= count_rows && col < count_columns. |
248 | pub fn set_horizontal_char(&mut self, pos: Position, c: char, offset: Offset) { |
249 | let chars = self |
250 | .horizontal_chars |
251 | .entry(pos) |
252 | .or_insert_with(|| HashMap::with_capacity(1)); |
253 | |
254 | chars.insert(offset, c); |
255 | } |
256 | |
257 | /// Get a list of overridden chars in a horizontal border. |
258 | /// |
259 | /// It takes not cell position but line as row and column of a cell; |
260 | /// So its range is line <= count_rows && col < count_columns. |
261 | pub fn lookup_horizontal_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> { |
262 | self.horizontal_chars |
263 | .get(&pos) |
264 | .and_then(|chars| { |
265 | chars.get(&Offset::Begin(offset)).or_else(|| { |
266 | if end > offset { |
267 | if end == 0 { |
268 | chars.get(&Offset::End(0)) |
269 | } else { |
270 | chars.get(&Offset::End(end - offset - 1)) |
271 | } |
272 | } else { |
273 | None |
274 | } |
275 | }) |
276 | }) |
277 | .copied() |
278 | } |
279 | |
280 | /// Checks if there any char in a horizontal border being overridden. |
281 | /// |
282 | /// It takes not cell position but line as row and column of a cell; |
283 | /// So its range is line <= count_rows && col < count_columns. |
284 | pub fn is_overridden_horizontal(&self, pos: Position) -> bool { |
285 | self.horizontal_chars.get(&pos).is_some() |
286 | } |
287 | |
288 | /// Removes a list of overridden chars in a horizontal border. |
289 | /// |
290 | /// It takes not cell position but line as row and column of a cell; |
291 | /// So its range is line <= count_rows && col < count_columns. |
292 | pub fn remove_overridden_horizontal(&mut self, pos: Position) { |
293 | self.horizontal_chars.remove(&pos); |
294 | } |
295 | |
296 | /// Override a vertical split line. |
297 | /// |
298 | /// If borders are not set the char won't be used. |
299 | /// |
300 | /// It takes not cell position but cell row and column of a line; |
301 | /// So its range is row < count_rows && col <= count_columns. |
302 | pub fn set_vertical_char(&mut self, pos: Position, c: char, offset: Offset) { |
303 | let chars = self |
304 | .vertical_chars |
305 | .entry(pos) |
306 | .or_insert_with(|| HashMap::with_capacity(1)); |
307 | |
308 | chars.insert(offset, c); |
309 | } |
310 | |
311 | /// Get a list of overridden chars in a horizontal border. |
312 | /// |
313 | /// It takes not cell position but cell row and column of a line; |
314 | /// So its range is row < count_rows && col <= count_columns. |
315 | pub fn lookup_vertical_char(&self, pos: Position, offset: usize, end: usize) -> Option<char> { |
316 | self.vertical_chars |
317 | .get(&pos) |
318 | .and_then(|chars| { |
319 | chars.get(&Offset::Begin(offset)).or_else(|| { |
320 | if end > offset { |
321 | if end == 0 { |
322 | chars.get(&Offset::End(0)) |
323 | } else { |
324 | chars.get(&Offset::End(end - offset - 1)) |
325 | } |
326 | } else { |
327 | None |
328 | } |
329 | }) |
330 | }) |
331 | .copied() |
332 | } |
333 | |
334 | /// Checks if there any char in a horizontal border being overridden. |
335 | /// |
336 | /// It takes not cell position but cell row and column of a line; |
337 | /// So its range is row < count_rows && col <= count_columns. |
338 | pub fn is_overridden_vertical(&self, pos: Position) -> bool { |
339 | self.vertical_chars.get(&pos).is_some() |
340 | } |
341 | |
342 | /// Removes a list of overridden chars in a horizontal border. |
343 | /// |
344 | /// It takes not cell position but cell row and column of a line; |
345 | /// So its range is row < count_rows && col <= count_columns. |
346 | pub fn remove_overridden_vertical(&mut self, pos: Position) { |
347 | self.vertical_chars.remove(&pos); |
348 | } |
349 | |
350 | /// Override a character color on a horizontal line. |
351 | pub fn set_horizontal_color(&mut self, pos: Position, c: ANSIBuf, offset: Offset) { |
352 | let chars = self |
353 | .horizontal_colors |
354 | .entry(pos) |
355 | .or_insert_with(|| HashMap::with_capacity(1)); |
356 | |
357 | chars.insert(offset, c); |
358 | } |
359 | |
360 | /// Get a overridden color in a horizontal border. |
361 | pub fn lookup_horizontal_color( |
362 | &self, |
363 | pos: Position, |
364 | offset: usize, |
365 | end: usize, |
366 | ) -> Option<&ANSIBuf> { |
367 | self.horizontal_colors.get(&pos).and_then(|chars| { |
368 | chars.get(&Offset::Begin(offset)).or_else(|| { |
369 | if end > offset { |
370 | if end == 0 { |
371 | chars.get(&Offset::End(0)) |
372 | } else { |
373 | chars.get(&Offset::End(end - offset - 1)) |
374 | } |
375 | } else { |
376 | None |
377 | } |
378 | }) |
379 | }) |
380 | } |
381 | |
382 | /// Override a character color on a vertical line. |
383 | pub fn set_vertical_color(&mut self, pos: Position, c: ANSIBuf, offset: Offset) { |
384 | let chars = self |
385 | .vertical_colors |
386 | .entry(pos) |
387 | .or_insert_with(|| HashMap::with_capacity(1)); |
388 | |
389 | chars.insert(offset, c); |
390 | } |
391 | |
392 | /// Get a overridden color in a vertical border. |
393 | pub fn lookup_vertical_color( |
394 | &self, |
395 | pos: Position, |
396 | offset: usize, |
397 | end: usize, |
398 | ) -> Option<&ANSIBuf> { |
399 | self.vertical_colors.get(&pos).and_then(|chars| { |
400 | chars.get(&Offset::Begin(offset)).or_else(|| { |
401 | if end > offset { |
402 | if end == 0 { |
403 | chars.get(&Offset::End(0)) |
404 | } else { |
405 | chars.get(&Offset::End(end - offset - 1)) |
406 | } |
407 | } else { |
408 | None |
409 | } |
410 | }) |
411 | }) |
412 | } |
413 | |
414 | /// Set a padding to a given cells. |
415 | pub fn set_padding(&mut self, entity: Entity, padding: Sides<Indent>) { |
416 | let mut pad = self.padding.get(entity).clone(); |
417 | pad.left.indent = padding.left; |
418 | pad.right.indent = padding.right; |
419 | pad.top.indent = padding.top; |
420 | pad.bottom.indent = padding.bottom; |
421 | |
422 | self.padding.insert(entity, pad); |
423 | } |
424 | |
425 | /// Set a padding to a given cells. |
426 | pub fn set_padding_color(&mut self, entity: Entity, padding: Sides<Option<ANSIBuf>>) { |
427 | let mut pad = self.padding.get(entity).clone(); |
428 | pad.left.color = padding.left; |
429 | pad.right.color = padding.right; |
430 | pad.top.color = padding.top; |
431 | pad.bottom.color = padding.bottom; |
432 | |
433 | self.padding.insert(entity, pad); |
434 | } |
435 | |
436 | /// Get a padding for a given [Entity]. |
437 | pub fn get_padding(&self, entity: Entity) -> Sides<Indent> { |
438 | let pad = self.padding.get(entity); |
439 | Sides::new( |
440 | pad.left.indent, |
441 | pad.right.indent, |
442 | pad.top.indent, |
443 | pad.bottom.indent, |
444 | ) |
445 | } |
446 | |
447 | /// Get a padding color for a given [Entity]. |
448 | pub fn get_padding_color(&self, entity: Entity) -> Sides<Option<ANSIBuf>> { |
449 | let pad = self.padding.get(entity); |
450 | Sides::new( |
451 | pad.left.color.clone(), |
452 | pad.right.color.clone(), |
453 | pad.top.color.clone(), |
454 | pad.bottom.color.clone(), |
455 | ) |
456 | } |
457 | |
458 | /// Set a formatting to a given cells. |
459 | pub fn set_formatting(&mut self, entity: Entity, formatting: Formatting) { |
460 | self.formatting.insert(entity, formatting); |
461 | } |
462 | |
463 | /// Get a formatting settings for a given [Entity]. |
464 | pub fn get_formatting(&self, entity: Entity) -> &Formatting { |
465 | self.formatting.get(entity) |
466 | } |
467 | |
468 | /// Set a vertical alignment to a given cells. |
469 | pub fn set_alignment_vertical(&mut self, entity: Entity, alignment: AlignmentVertical) { |
470 | self.alignment_v.insert(entity, alignment); |
471 | } |
472 | |
473 | /// Get a vertical alignment for a given [Entity]. |
474 | pub fn get_alignment_vertical(&self, entity: Entity) -> &AlignmentVertical { |
475 | self.alignment_v.get(entity) |
476 | } |
477 | |
478 | /// Set a horizontal alignment to a given cells. |
479 | pub fn set_alignment_horizontal(&mut self, entity: Entity, alignment: AlignmentHorizontal) { |
480 | self.alignment_h.insert(entity, alignment); |
481 | } |
482 | |
483 | /// Get a horizontal alignment for a given [Entity]. |
484 | pub fn get_alignment_horizontal(&self, entity: Entity) -> &AlignmentHorizontal { |
485 | self.alignment_h.get(entity) |
486 | } |
487 | |
488 | /// Set border set a border value to all cells in [`Entity`]. |
489 | pub fn set_border(&mut self, pos: Position, border: Border<char>) { |
490 | self.borders.insert_border(pos, border); |
491 | } |
492 | |
493 | /// Returns a border of a cell. |
494 | pub fn get_border(&self, pos: Position, shape: (usize, usize)) -> Border<char> { |
495 | self.borders.get_border(pos, shape).copied() |
496 | } |
497 | |
498 | /// Returns a border color of a cell. |
499 | pub fn get_border_color(&self, pos: Position, shape: (usize, usize)) -> Border<&ANSIBuf> { |
500 | self.borders_colors.get_border(pos, shape) |
501 | } |
502 | |
503 | /// Set a character which will be used in case any misconfiguration of borders. |
504 | /// |
505 | /// It will be usde for example when you set a left char for border frame and top but didn't set a top left corner. |
506 | pub fn set_borders_missing(&mut self, c: char) { |
507 | self.borders_missing_char = c; |
508 | } |
509 | |
510 | /// Get a character which will be used in case any misconfiguration of borders. |
511 | pub fn get_borders_missing(&self) -> char { |
512 | self.borders_missing_char |
513 | } |
514 | |
515 | /// Gets a color of all borders on the grid. |
516 | pub fn get_border_color_default(&self) -> Option<&ANSIBuf> { |
517 | self.borders_colors.get_global() |
518 | } |
519 | |
520 | /// Sets a color of all borders on the grid. |
521 | pub fn set_border_color_default(&mut self, clr: ANSIBuf) { |
522 | self.borders_colors = BordersConfig::default(); |
523 | self.borders_colors.set_global(clr); |
524 | } |
525 | |
526 | /// Gets colors of a borders carcass on the grid. |
527 | pub fn get_color_borders(&self) -> &Borders<ANSIBuf> { |
528 | self.borders_colors.get_borders() |
529 | } |
530 | |
531 | /// Sets colors of border carcass on the grid. |
532 | pub fn set_borders_color(&mut self, clrs: Borders<ANSIBuf>) { |
533 | self.borders_colors.set_borders(clrs); |
534 | } |
535 | |
536 | /// Sets a color of border of a cell on the grid. |
537 | pub fn set_border_color(&mut self, pos: Position, border: Border<ANSIBuf>) { |
538 | self.borders_colors.insert_border(pos, border) |
539 | } |
540 | |
541 | /// Sets off all borders possible on the [`Entity`]. |
542 | /// |
543 | /// It doesn't changes globally set borders through [`SpannedConfig::set_borders`]. |
544 | // |
545 | // todo: would be great to remove a shape |
546 | pub fn remove_border(&mut self, pos: Position, shape: (usize, usize)) { |
547 | self.borders.remove_border(pos, shape); |
548 | } |
549 | |
550 | /// Gets a color of border of a cell on the grid. |
551 | // |
552 | // todo: would be great to remove a shape |
553 | pub fn remove_border_color(&mut self, pos: Position, shape: (usize, usize)) { |
554 | self.borders_colors.remove_border(pos, shape); |
555 | } |
556 | |
557 | /// Get a justification which will be used while expanding cells width/height. |
558 | pub fn get_justification(&self, entity: Entity) -> char { |
559 | *self.justification.get(entity) |
560 | } |
561 | |
562 | /// Get a justification color which will be used while expanding cells width/height. |
563 | /// |
564 | /// `None` means no color. |
565 | pub fn get_justification_color(&self, entity: Entity) -> Option<&ANSIBuf> { |
566 | self.justification_color.get(entity).as_ref() |
567 | } |
568 | |
569 | /// Set a justification which will be used while expanding cells width/height. |
570 | pub fn set_justification(&mut self, entity: Entity, c: char) { |
571 | self.justification.insert(entity, c); |
572 | } |
573 | |
574 | /// Set a justification color which will be used while expanding cells width/height. |
575 | /// |
576 | /// `None` removes it. |
577 | pub fn set_justification_color(&mut self, entity: Entity, color: Option<ANSIBuf>) { |
578 | self.justification_color.insert(entity, color); |
579 | } |
580 | |
581 | /// Get a span value of the cell, if any is set. |
582 | pub fn get_column_spans(&self) -> HashMap<Position, usize> { |
583 | self.span_columns.clone() |
584 | } |
585 | |
586 | /// Get a span value of the cell, if any is set. |
587 | pub fn get_row_spans(&self) -> HashMap<Position, usize> { |
588 | self.span_rows.clone() |
589 | } |
590 | |
591 | /// Get a span value of the cell, if any is set. |
592 | pub fn get_column_span(&self, pos: Position) -> Option<usize> { |
593 | self.span_columns.get(&pos).copied() |
594 | } |
595 | |
596 | /// Get a span value of the cell, if any is set. |
597 | pub fn get_row_span(&self, pos: Position) -> Option<usize> { |
598 | self.span_rows.get(&pos).copied() |
599 | } |
600 | |
601 | /// Removes column spans. |
602 | pub fn remove_column_spans(&mut self) { |
603 | self.span_columns.clear() |
604 | } |
605 | |
606 | /// Removes row spans. |
607 | pub fn remove_row_spans(&mut self) { |
608 | self.span_rows.clear() |
609 | } |
610 | |
611 | /// Set a column span to a given cells. |
612 | /// |
613 | /// BEWARE |
614 | /// |
615 | /// IT'S CALLER RESPONSIBILITY TO MAKE SURE |
616 | /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT |
617 | pub fn set_column_span(&mut self, pos: Position, span: usize) { |
618 | set_cell_column_span(self, pos, span); |
619 | } |
620 | |
621 | /// Verifies if there's any spans set. |
622 | pub fn has_column_spans(&self) -> bool { |
623 | !self.span_columns.is_empty() |
624 | } |
625 | |
626 | /// Set a column span to a given cells. |
627 | /// |
628 | /// BEWARE |
629 | /// |
630 | /// IT'S CALLER RESPONSIBILITY TO MAKE SURE |
631 | /// THAT THERE NO INTERSECTIONS IN PLACE AND THE SPAN VALUE IS CORRECT |
632 | pub fn set_row_span(&mut self, pos: Position, span: usize) { |
633 | set_cell_row_span(self, pos, span); |
634 | } |
635 | |
636 | /// Verifies if there's any spans set. |
637 | pub fn has_row_spans(&self) -> bool { |
638 | !self.span_rows.is_empty() |
639 | } |
640 | |
641 | /// Verifies if there's any colors set for a borders. |
642 | pub fn has_border_colors(&self) -> bool { |
643 | !self.borders_colors.is_empty() |
644 | } |
645 | |
646 | /// Verifies if there's any colors set for a borders. |
647 | pub fn has_offset_chars(&self) -> bool { |
648 | !self.horizontal_chars.is_empty() || !self.vertical_chars.is_empty() |
649 | } |
650 | |
651 | /// Verifies if there's any colors set for a borders. |
652 | pub fn has_justification(&self) -> bool { |
653 | !self.justification.is_empty() || !self.justification_color.is_empty() |
654 | } |
655 | |
656 | /// Verifies if there's any custom padding set. |
657 | pub fn has_padding(&self) -> bool { |
658 | !self.padding.is_empty() |
659 | } |
660 | |
661 | /// Verifies if there's any custom padding set. |
662 | pub fn has_padding_color(&self) -> bool { |
663 | let map = HashMap::from(self.padding.clone()); |
664 | let mut has_color = false; |
665 | for (entity, value) in map { |
666 | if matches!(entity, Entity::Global) { |
667 | continue; |
668 | } |
669 | |
670 | has_color = value.bottom.color.is_some() |
671 | || value.top.color.is_some() |
672 | || value.left.color.is_some() |
673 | || value.right.color.is_some(); |
674 | |
675 | if has_color { |
676 | break; |
677 | } |
678 | } |
679 | |
680 | has_color |
681 | } |
682 | |
683 | /// Verifies if there's any custom formatting set. |
684 | pub fn has_formatting(&self) -> bool { |
685 | !self.formatting.is_empty() |
686 | } |
687 | |
688 | /// Verifies if there's any custom alignment vertical set. |
689 | pub fn has_alignemnt_vertical(&self) -> bool { |
690 | !self.alignment_v.is_empty() |
691 | } |
692 | |
693 | /// Verifies if there's any custom alignment horizontal set. |
694 | pub fn has_alignemnt_horizontal(&self) -> bool { |
695 | !self.alignment_h.is_empty() |
696 | } |
697 | |
698 | /// Gets an intersection character which would be rendered on the grid. |
699 | /// |
700 | /// grid: crate::Grid |
701 | pub fn get_intersection(&self, pos: Position, shape: (usize, usize)) -> Option<char> { |
702 | let c = self.borders.get_intersection(pos, shape); |
703 | if let Some(c) = c { |
704 | return Some(*c); |
705 | } |
706 | |
707 | if self.has_horizontal(pos.0, shape.0) && self.has_vertical(pos.1, shape.1) { |
708 | return Some(self.get_borders_missing()); |
709 | } |
710 | |
711 | None |
712 | } |
713 | |
714 | /// Gets a horizontal character which would be rendered on the grid. |
715 | /// |
716 | /// grid: crate::Grid |
717 | pub fn get_horizontal(&self, pos: Position, count_rows: usize) -> Option<char> { |
718 | let c = self.borders.get_horizontal(pos, count_rows); |
719 | if let Some(c) = c { |
720 | return Some(*c); |
721 | } |
722 | |
723 | if self.has_horizontal(pos.0, count_rows) { |
724 | return Some(self.get_borders_missing()); |
725 | } |
726 | |
727 | None |
728 | } |
729 | |
730 | /// Gets a vertical character which would be rendered on the grid. |
731 | /// |
732 | /// grid: crate::Grid |
733 | pub fn get_vertical(&self, pos: Position, count_columns: usize) -> Option<char> { |
734 | if let Some(c) = self.borders.get_vertical(pos, count_columns) { |
735 | return Some(*c); |
736 | } |
737 | |
738 | if self.has_vertical(pos.1, count_columns) { |
739 | return Some(self.get_borders_missing()); |
740 | } |
741 | |
742 | None |
743 | } |
744 | |
745 | /// Gets a color of a cell horizontal. |
746 | pub fn get_horizontal_color(&self, pos: Position, count_rows: usize) -> Option<&ANSIBuf> { |
747 | self.borders_colors.get_horizontal(pos, count_rows) |
748 | } |
749 | |
750 | /// Gets a color of a cell vertical. |
751 | pub fn get_vertical_color(&self, pos: Position, count_columns: usize) -> Option<&ANSIBuf> { |
752 | self.borders_colors.get_vertical(pos, count_columns) |
753 | } |
754 | |
755 | /// Gets a color of a cell vertical. |
756 | pub fn get_intersection_color(&self, pos: Position, shape: (usize, usize)) -> Option<&ANSIBuf> { |
757 | self.borders_colors.get_intersection(pos, shape) |
758 | } |
759 | |
760 | /// Checks if grid would have a horizontal border with the current configuration. |
761 | /// |
762 | /// grid: crate::Grid |
763 | pub fn has_horizontal(&self, row: usize, count_rows: usize) -> bool { |
764 | self.borders.has_horizontal(row, count_rows) |
765 | } |
766 | |
767 | /// Checks if grid would have a vertical border with the current configuration. |
768 | /// |
769 | /// grid: crate::Grid |
770 | pub fn has_vertical(&self, col: usize, count_columns: usize) -> bool { |
771 | self.borders.has_vertical(col, count_columns) |
772 | } |
773 | |
774 | /// Calculates an amount of horizontal lines would present on the grid. |
775 | /// |
776 | /// grid: crate::Grid |
777 | pub fn count_horizontal(&self, count_rows: usize) -> usize { |
778 | (0..=count_rows) |
779 | .filter(|&row| self.has_horizontal(row, count_rows)) |
780 | .count() |
781 | } |
782 | |
783 | /// Calculates an amount of vertical lines would present on the grid. |
784 | /// |
785 | /// grid: crate::Grid |
786 | pub fn count_vertical(&self, count_columns: usize) -> usize { |
787 | (0..=count_columns) |
788 | .filter(|&col| self.has_vertical(col, count_columns)) |
789 | .count() |
790 | } |
791 | |
792 | /// The function returns whether the cells will be rendered or it will be hidden because of a span. |
793 | pub fn is_cell_visible(&self, pos: Position) -> bool { |
794 | !(self.is_cell_covered_by_column_span(pos) |
795 | || self.is_cell_covered_by_row_span(pos) |
796 | || self.is_cell_covered_by_both_spans(pos)) |
797 | } |
798 | |
799 | /// The function checks if a cell is hidden because of a row span. |
800 | pub fn is_cell_covered_by_row_span(&self, pos: Position) -> bool { |
801 | is_cell_covered_by_row_span(self, pos) |
802 | } |
803 | |
804 | /// The function checks if a cell is hidden because of a column span. |
805 | pub fn is_cell_covered_by_column_span(&self, pos: Position) -> bool { |
806 | is_cell_covered_by_column_span(self, pos) |
807 | } |
808 | |
809 | /// The function checks if a cell is hidden indirectly because of a row and column span combination. |
810 | pub fn is_cell_covered_by_both_spans(&self, pos: Position) -> bool { |
811 | is_cell_covered_by_both_spans(self, pos) |
812 | } |
813 | } |
814 | |
815 | impl From<CompactConfig> for SpannedConfig { |
816 | fn from(compact: CompactConfig) -> Self { |
817 | use Entity::Global; |
818 | |
819 | let mut cfg: SpannedConfig = Self::default(); |
820 | |
821 | cfg.set_padding(entity:Global, *compact.get_padding()); |
822 | cfg.set_padding_color(entity:Global, padding:to_ansi_color(*compact.get_padding_color())); |
823 | cfg.set_margin(*compact.get_margin()); |
824 | cfg.set_margin_color(margin:to_ansi_color(*compact.get_margin_color())); |
825 | cfg.set_alignment_horizontal(entity:Global, alignment:compact.get_alignment_horizontal()); |
826 | cfg.set_borders(*compact.get_borders()); |
827 | cfg.set_borders_color(clrs:compact.get_borders_color().convert_into()); |
828 | |
829 | cfg |
830 | } |
831 | } |
832 | |
833 | fn to_ansi_color(b: Sides<ANSIStr<'_>>) -> Sides<Option<ANSIBuf>> { |
834 | Sides::new( |
835 | left:Some(b.left.into()), |
836 | right:Some(b.right.into()), |
837 | top:Some(b.top.into()), |
838 | bottom:Some(b.bottom.into()), |
839 | ) |
840 | } |
841 | |
842 | fn set_cell_row_span(cfg: &mut SpannedConfig, pos: Position, span: usize) { |
843 | // such spans aren't supported |
844 | if span == 0 { |
845 | return; |
846 | } |
847 | |
848 | // It's a default span so we can do nothing. |
849 | // but we check if it's an override of a span. |
850 | if span == 1 { |
851 | cfg.span_rows.remove(&pos); |
852 | return; |
853 | } |
854 | |
855 | cfg.span_rows.insert(k:pos, v:span); |
856 | } |
857 | |
858 | fn set_cell_column_span(cfg: &mut SpannedConfig, pos: Position, span: usize) { |
859 | // such spans aren't supported |
860 | if span == 0 { |
861 | return; |
862 | } |
863 | |
864 | // It's a default span so we can do nothing. |
865 | // but we check if it's an override of a span. |
866 | if span == 1 { |
867 | cfg.span_columns.remove(&pos); |
868 | return; |
869 | } |
870 | |
871 | cfg.span_columns.insert(k:pos, v:span); |
872 | } |
873 | |
874 | fn is_cell_covered_by_column_span(cfg: &SpannedConfig, pos: Position) -> bool { |
875 | cfgIter<'_, (usize, usize), …>.span_columns |
876 | .iter() |
877 | .any(|(&(row: usize, col: usize), span: &usize)| pos.1 > col && pos.1 < col + span && row == pos.0) |
878 | } |
879 | |
880 | fn is_cell_covered_by_row_span(cfg: &SpannedConfig, pos: Position) -> bool { |
881 | cfgIter<'_, (usize, usize), …>.span_rows |
882 | .iter() |
883 | .any(|(&(row: usize, col: usize), span: &usize)| pos.0 > row && pos.0 < row + span && col == pos.1) |
884 | } |
885 | |
886 | fn is_cell_covered_by_both_spans(cfg: &SpannedConfig, pos: Position) -> bool { |
887 | if !cfg.has_column_spans() || !cfg.has_row_spans() { |
888 | return false; |
889 | } |
890 | |
891 | cfg.span_rows.iter().any(|(p1: &(usize, usize), row_span: &usize)| { |
892 | cfgimpl Iterator .span_columns |
893 | .iter() |
894 | .filter(|(p2: &&(usize, usize), _)| &p1 == p2) |
895 | .any(|(_, col_span: &usize)| { |
896 | pos.0 > p1.0 && pos.0 < p1.0 + row_span && pos.1 > p1.1 && pos.1 < p1.1 + col_span |
897 | }) |
898 | }) |
899 | } |
900 | |
901 | #[derive (Default, Debug, Clone, PartialEq, Eq)] |
902 | struct ColoredIndent { |
903 | indent: Indent, |
904 | color: Option<ANSIBuf>, |
905 | } |
906 | |
907 | /// A colorefull margin indent. |
908 | #[derive (Debug, Clone, PartialEq, Eq)] |
909 | struct ColoredMarginIndent { |
910 | /// An indent value. |
911 | indent: Indent, |
912 | /// An offset value. |
913 | offset: Offset, |
914 | /// An color value. |
915 | color: Option<ANSIBuf>, |
916 | } |
917 | |
918 | impl Default for ColoredMarginIndent { |
919 | fn default() -> Self { |
920 | Self { |
921 | indent: Indent::default(), |
922 | offset: Offset::Begin(0), |
923 | color: None, |
924 | } |
925 | } |
926 | } |
927 | |