tabled/settings/padding_expand/
mod.rs

1//! This module contains a [`PaddingExpand`] setting to expand cells padding to its limit a cell.
2
3use papergrid::{
4    config::{AlignmentHorizontal, AlignmentVertical},
5    dimension::iterable::IterGridDimension,
6    records::{ExactRecords, IntoRecords, PeekableRecords, Records},
7};
8
9#[cfg(feature = "std")]
10use crate::{
11    grid::{config::ColoredConfig, config::Entity},
12    settings::CellOption,
13};
14
15use super::TableOption;
16
17/// PaddingExpand is able to expand padding to its limit a cell.
18/// Maybe usefull in cases were its colorization is supposed to be used next.
19///
20/// # Example
21///
22#[cfg_attr(feature = "ansi", doc = "```")]
23#[cfg_attr(not(feature = "ansi"), doc = "```ignore")]
24/// use std::iter::FromIterator;
25///
26/// use tabled::{
27///     settings::{Padding, Style, Color, PaddingColor},
28///     Table,
29/// };
30///
31/// let char_split = |s: &str| s.chars().map(|c| c.to_string()).collect::<Vec<_>>();
32/// let data = vec![
33///     char_split("2021"),
34///     char_split("2022"),
35///     char_split("2023"),
36///     char_split("2024"),
37/// ];
38///
39/// let table = Table::from_iter(&data)
40///     .with(Style::ascii())
41///     .with(PaddingColor::filled(Color::BG_BLUE))
42///     .modify((2, 1), Padding::new(2, 2, 3, 3))
43///     .with(Padding::expand(true))
44///     .with(Padding::expand(false))
45///     .to_string();
46///
47/// assert_eq!(
48///     table,
49///     concat!(
50///         "+---+-----+---+---+\n",
51///         "|2\u{1b}[44m  \u{1b}[49m|0\u{1b}[44m    \u{1b}[49m|2\u{1b}[44m  \u{1b}[49m|1\u{1b}[44m  \u{1b}[49m|\n",
52///         "+---+-----+---+---+\n",
53///         "|2\u{1b}[44m  \u{1b}[49m|0\u{1b}[44m    \u{1b}[49m|2\u{1b}[44m  \u{1b}[49m|2\u{1b}[44m  \u{1b}[49m|\n",
54///         "+---+-----+---+---+\n",
55///         "|2\u{1b}[44m  \u{1b}[49m|0\u{1b}[44m    \u{1b}[49m|2\u{1b}[44m  \u{1b}[49m|3\u{1b}[44m  \u{1b}[49m|\n",
56///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
57///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
58///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
59///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
60///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
61///         "|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m     \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\u{1b}[44m   \u{1b}[49m|\n",
62///         "+---+-----+---+---+\n",
63///         "|2\u{1b}[44m  \u{1b}[49m|0\u{1b}[44m    \u{1b}[49m|2\u{1b}[44m  \u{1b}[49m|4\u{1b}[44m  \u{1b}[49m|\n",
64///         "+---+-----+---+---+",
65///     ),
66/// );
67/// ```
68#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
69pub enum PaddingExpand {
70    /// Horizontal expansion of padding (LEFT and RIGHT)
71    Horizontal,
72    /// Vertical expansion of padding (TOP and BOTTOM)
73    Vertical,
74}
75
76impl<R, D> TableOption<R, ColoredConfig, D> for PaddingExpand
77where
78    R: Records + ExactRecords + PeekableRecords,
79    for<'a> &'a R: Records,
80    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
81{
82    fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
83        <Self as CellOption<R, ColoredConfig>>::change(self, records, cfg, Entity::Global)
84    }
85}
86
87impl<R> CellOption<R, ColoredConfig> for PaddingExpand
88where
89    R: Records + ExactRecords + PeekableRecords,
90    for<'a> &'a R: Records,
91    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
92{
93    fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
94        match self {
95            PaddingExpand::Vertical => expand_vertical(records, cfg, entity),
96            PaddingExpand::Horizontal => expand_horizontal(records, cfg, entity),
97        }
98    }
99}
100
101fn expand_horizontal<R>(records: &mut R, cfg: &mut ColoredConfig, entity: Entity)
102where
103    R: Records + ExactRecords + PeekableRecords,
104    for<'a> &'a R: Records,
105    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
106{
107    let widths = IterGridDimension::width(&*records, cfg);
108
109    let count_rows = records.count_rows();
110    let count_cols = records.count_columns();
111
112    for pos in entity.iter(count_rows, count_cols) {
113        let col = pos.col;
114        let column_width = widths[col];
115        let width = records.get_width(pos);
116
117        if width < column_width {
118            let alignment = *cfg.get_alignment_horizontal(pos);
119
120            let available_width = column_width - width;
121            let (left, right) = split_horizontal_space(alignment, available_width);
122
123            let mut pad = *cfg.get_padding(pos);
124            pad.left.size = left;
125            pad.right.size = right;
126
127            cfg.set_padding(Entity::from(pos), pad);
128        }
129    }
130}
131
132fn expand_vertical<R>(records: &mut R, cfg: &mut ColoredConfig, entity: Entity)
133where
134    R: Records + ExactRecords + PeekableRecords,
135    for<'a> &'a R: Records,
136    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
137{
138    let heights = IterGridDimension::height(&*records, cfg);
139
140    let count_rows = records.count_rows();
141    let count_cols = records.count_columns();
142
143    for pos in entity.iter(count_rows, count_cols) {
144        let row = pos.row;
145        let row_height = heights[row];
146        let cell_height = records.count_lines(pos);
147
148        if cell_height < row_height {
149            let alignment = *cfg.get_alignment_vertical(pos);
150
151            let available_width = row_height - cell_height;
152            let (top, bottom) = split_vertical_space(alignment, available_width);
153
154            let mut pad = *cfg.get_padding(pos);
155            pad.top.size = top;
156            pad.bottom.size = bottom;
157
158            cfg.set_padding(Entity::from(pos), pad);
159        }
160    }
161}
162
163fn split_horizontal_space(al: AlignmentHorizontal, space: usize) -> (usize, usize) {
164    match al {
165        AlignmentHorizontal::Center => {
166            let left = space / 2;
167            let right = space - left;
168            (left, right)
169        }
170        AlignmentHorizontal::Left => (0, space),
171        AlignmentHorizontal::Right => (space, 0),
172    }
173}
174
175fn split_vertical_space(al: AlignmentVertical, space: usize) -> (usize, usize) {
176    match al {
177        AlignmentVertical::Center => {
178            let left = space / 2;
179            let right = space - left;
180            (left, right)
181        }
182        AlignmentVertical::Top => (0, space),
183        AlignmentVertical::Bottom => (space, 0),
184    }
185}