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