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