tabled/settings/themes/
border_correction.rs1use crate::{
7 grid::{
8 config::{ColoredConfig, Position, SpannedConfig},
9 records::{ExactRecords, Records},
10 },
11 settings::TableOption,
12};
13
14#[derive(Debug)]
72pub struct BorderCorrection {}
73
74impl BorderCorrection {
75 pub fn span() -> Self {
78 Self {}
79 }
80}
81
82impl<R, D> TableOption<R, ColoredConfig, D> for BorderCorrection
83where
84 R: Records + ExactRecords,
85{
86 fn change(self, records: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
87 let shape = (records.count_rows(), records.count_columns());
88 correct_span_styles(cfg, shape);
89 }
90}
91
92fn correct_span_styles(cfg: &mut SpannedConfig, shape: (usize, usize)) {
93 for (p, span) in cfg.get_column_spans() {
94 for col in p.col..p.col + span {
95 if col == 0 {
96 continue;
97 }
98
99 let is_first = col == p.col;
100 let has_up = p.row > 0 && has_left(cfg, (p.row - 1, col).into(), shape);
101 let has_down = p.row + 1 < shape.0 && has_left(cfg, (p.row + 1, col).into(), shape);
102
103 let borders = cfg.get_borders();
104
105 let mut border = cfg.get_border((p.row, col).into(), shape);
106
107 let has_top_border = border.left_top_corner.is_some() && border.top.is_some();
108 if has_top_border {
109 if has_up && is_first {
110 border.left_top_corner = borders.intersection;
111 } else if has_up {
112 border.left_top_corner = borders.bottom_intersection;
113 } else if is_first {
114 border.left_top_corner = borders.top_intersection;
115 } else {
116 border.left_top_corner = border.top;
117 }
118 }
119
120 let has_bottom_border = border.left_bottom_corner.is_some() && border.bottom.is_some();
121 if has_bottom_border {
122 if has_down && is_first {
123 border.left_bottom_corner = borders.intersection;
124 } else if has_down {
125 border.left_bottom_corner = borders.top_intersection;
126 } else if is_first {
127 border.left_bottom_corner = borders.bottom_intersection;
128 } else {
129 border.left_bottom_corner = border.bottom;
130 }
131 }
132
133 cfg.set_border((p.row, col).into(), border);
134 }
135 }
136
137 for (p, span) in cfg.get_row_spans() {
138 let (r, col) = p.into();
139
140 for row in r + 1..r + span {
141 let mut border = cfg.get_border((row, col).into(), shape);
142 let borders = cfg.get_borders();
143
144 let has_left_border = border.left_top_corner.is_some();
145 if has_left_border {
146 let has_left = col > 0 && has_top(cfg, (row, col - 1).into(), shape);
147 if has_left {
148 border.left_top_corner = borders.right_intersection;
149 } else {
150 border.left_top_corner = borders.vertical;
151 }
152 }
153
154 let has_right_border = border.right_top_corner.is_some();
155 if has_right_border {
156 let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1).into(), shape);
157 if has_right {
158 border.right_top_corner = borders.left_intersection;
159 } else {
160 border.right_top_corner = borders.vertical;
161 }
162 }
163
164 cfg.set_border((row, col).into(), border);
165 }
166 }
167
168 let cells = iter_totally_spanned_cells(cfg, shape).collect::<Vec<_>>();
169 for p in cells {
170 let (row, col) = p.into();
171
172 if row == 0 {
173 continue;
174 }
175
176 let mut border = cfg.get_border((row, col).into(), shape);
177 let borders = cfg.get_borders();
178
179 let has_right = col + 1 < shape.1 && has_top(cfg, (row, col + 1).into(), shape);
180 let has_up = has_left(cfg, (row - 1, col).into(), shape);
181
182 if has_up && !has_right {
183 border.right_top_corner = borders.right_intersection;
184 }
185
186 if !has_up && has_right {
187 border.right_top_corner = borders.left_intersection;
188 }
189
190 let has_down = row + 1 < shape.0 && has_left(cfg, (row + 1, col).into(), shape);
191 if has_down {
192 border.left_bottom_corner = borders.top_intersection;
193 }
194
195 cfg.set_border((row, col).into(), border);
196 }
197}
198
199fn has_left(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
200 if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_column_span(pos) {
201 return false;
202 }
203
204 let border = cfg.get_border(pos, shape);
205 border.left.is_some() || border.left_top_corner.is_some() || border.left_bottom_corner.is_some()
206}
207
208fn has_top(cfg: &SpannedConfig, pos: Position, shape: (usize, usize)) -> bool {
209 if cfg.is_cell_covered_by_both_spans(pos) || cfg.is_cell_covered_by_row_span(pos) {
210 return false;
211 }
212
213 let border = cfg.get_border(pos, shape);
214 border.top.is_some() || border.left_top_corner.is_some() || border.right_top_corner.is_some()
215}
216
217fn iter_totally_spanned_cells(
218 cfg: &SpannedConfig,
219 shape: (usize, usize),
220) -> impl Iterator<Item = Position> + '_ {
221 let (count_rows, count_cols) = shape;
223 (0..count_rows).flat_map(move |row| {
224 (0..count_cols)
225 .map(move |col| (row, col).into())
226 .filter(move |p| cfg.is_cell_covered_by_both_spans(*p))
227 })
228}