1use paperforge_core::{Color, Rect};
2
3use crate::element::Align;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum BorderStyle {
7 None,
8 Solid,
9 Dashed,
10 Dotted,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct Border {
15 pub top: BorderStyle,
16 pub bottom: BorderStyle,
17 pub left: BorderStyle,
18 pub right: BorderStyle,
19 pub width: f64,
20 pub color: Color,
21}
22
23impl Default for Border {
24 fn default() -> Self {
25 Self {
26 top: BorderStyle::None,
27 bottom: BorderStyle::None,
28 left: BorderStyle::None,
29 right: BorderStyle::None,
30 width: 1.0,
31 color: Color::black(),
32 }
33 }
34}
35
36#[derive(Debug, Clone)]
37pub struct Cell {
38 pub text: String,
39 pub align: Align,
40 pub font_size: f64,
41 pub color: Color,
42 pub border: Border,
43 pub bg_color: Option<Color>,
44 pub padding: f64,
45 pub col_span: u32,
46 pub row_span: u32,
47}
48
49impl Cell {
50 pub fn new(text: &str) -> Self {
51 Self {
52 text: text.to_string(),
53 align: Align::Left,
54 font_size: 11.0,
55 color: Color::black(),
56 border: Border::default(),
57 bg_color: None,
58 padding: 5.0,
59 col_span: 1,
60 row_span: 1,
61 }
62 }
63
64 pub fn align(mut self, align: Align) -> Self {
65 self.align = align;
66 self
67 }
68
69 pub fn font_size(mut self, size: f64) -> Self {
70 self.font_size = size;
71 self
72 }
73
74 pub fn color(mut self, color: Color) -> Self {
75 self.color = color;
76 self
77 }
78
79 pub fn border(mut self, border: Border) -> Self {
80 self.border = border;
81 self
82 }
83
84 pub fn bg_color(mut self, color: Color) -> Self {
85 self.bg_color = Some(color);
86 self
87 }
88
89 pub fn padding(mut self, padding: f64) -> Self {
90 self.padding = padding;
91 self
92 }
93
94 pub fn col_span(mut self, span: u32) -> Self {
95 self.col_span = span;
96 self
97 }
98
99 pub fn row_span(mut self, span: u32) -> Self {
100 self.row_span = span;
101 self
102 }
103}
104
105impl Default for Cell {
106 fn default() -> Self {
107 Self::new("")
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Default)]
112pub enum HeaderPosition {
113 #[default]
114 Top,
115 Bottom,
116 None,
117}
118
119#[derive(Debug, Clone)]
120pub struct TableConfig {
121 pub headers: Vec<Cell>,
122 pub rows: Vec<Vec<Cell>>,
123 pub column_widths: Vec<f64>,
124 pub row_height: f64,
125 pub header_position: HeaderPosition,
126 pub border: Border,
127 pub header_bg: Option<Color>,
128 pub width: f64,
129 pub x: f64,
130 pub y: f64,
131}
132
133impl TableConfig {
134 pub fn new() -> Self {
135 Self {
136 headers: Vec::new(),
137 rows: Vec::new(),
138 column_widths: Vec::new(),
139 row_height: 20.0,
140 header_position: HeaderPosition::Top,
141 border: Border::default(),
142 header_bg: None,
143 width: 400.0,
144 x: 50.0,
145 y: 700.0,
146 }
147 }
148
149 pub fn headers(mut self, headers: Vec<Cell>) -> Self {
150 self.headers = headers;
151 self
152 }
153
154 pub fn header_labels(mut self, labels: &[&str]) -> Self {
155 self.headers = labels.iter().map(|s| Cell::new(s)).collect();
156 self
157 }
158
159 pub fn rows(mut self, rows: Vec<Vec<Cell>>) -> Self {
160 self.rows = rows;
161 self
162 }
163
164 pub fn row(mut self, cells: Vec<Cell>) -> Self {
165 self.rows.push(cells);
166 self
167 }
168
169 pub fn column_widths(mut self, widths: Vec<f64>) -> Self {
170 self.column_widths = widths;
171 self
172 }
173
174 pub fn auto_width(self, total_width: f64, n_cols: usize) -> Self {
175 let width = total_width / n_cols as f64;
176 let column_widths = vec![width; n_cols];
177 TableConfig {
178 column_widths,
179 ..self
180 }
181 }
182
183 pub fn row_height(mut self, height: f64) -> Self {
184 self.row_height = height;
185 self
186 }
187
188 pub fn header_position(mut self, pos: HeaderPosition) -> Self {
189 self.header_position = pos;
190 self
191 }
192
193 pub fn border(mut self, border: Border) -> Self {
194 self.border = border;
195 self
196 }
197
198 pub fn header_bg(mut self, color: Color) -> Self {
199 self.header_bg = Some(color);
200 self
201 }
202
203 pub fn position(mut self, x: f64, y: f64) -> Self {
204 self.x = x;
205 self.y = y;
206 self
207 }
208
209 pub fn width(mut self, w: f64) -> Self {
210 self.width = w;
211 self
212 }
213
214 pub fn compute_widths(&self) -> Vec<f64> {
215 if !self.column_widths.is_empty() {
216 return self.column_widths.clone();
217 }
218 let n_cols = self
219 .headers
220 .len()
221 .max(self.rows.iter().map(|r| r.len()).max().unwrap_or(0));
222 if n_cols == 0 {
223 return Vec::new();
224 }
225 vec![self.width / n_cols as f64; n_cols]
226 }
227
228 pub fn column_count(&self) -> usize {
229 let header_cols = self.headers.len();
230 let max_row_cols = self.rows.iter().map(|r| r.len()).max().unwrap_or(0);
231 header_cols.max(max_row_cols)
232 }
233
234 pub fn cell_bounds(&self, row: usize, col: usize) -> Option<Rect> {
235 let widths = self.compute_widths();
236 let n_cols = widths.len();
237 if col >= n_cols {
238 return None;
239 }
240 let x = self.x + (0..col).map(|i| widths[i]).sum::<f64>();
241 let y = self.y - (row as f64) * self.row_height;
242 Some(Rect::new(x, y, widths[col], self.row_height))
243 }
244}
245
246impl Default for TableConfig {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252pub struct TableBuilder {
253 table: TableConfig,
254}
255
256impl TableBuilder {
257 pub fn new() -> Self {
258 Self {
259 table: TableConfig::new(),
260 }
261 }
262
263 pub fn headers(mut self, header_labels: &[&str]) -> Self {
264 self.table.headers = header_labels.iter().map(|s| Cell::new(s)).collect();
265 self
266 }
267
268 pub fn header_cells(mut self, cells: Vec<Cell>) -> Self {
269 self.table.headers = cells;
270 self
271 }
272
273 pub fn row(mut self, cells: Vec<Cell>) -> Self {
274 self.table.rows.push(cells);
275 self
276 }
277
278 pub fn row_str(mut self, cells: &[&str]) -> Self {
279 self.table
280 .rows
281 .push(cells.iter().map(|s| Cell::new(s)).collect());
282 self
283 }
284
285 pub fn column_widths(mut self, widths: Vec<f64>) -> Self {
286 self.table.column_widths = widths;
287 self
288 }
289
290 pub fn row_height(mut self, height: f64) -> Self {
291 self.table.row_height = height;
292 self
293 }
294
295 pub fn border(mut self, border: Border) -> Self {
296 self.table.border = border;
297 self
298 }
299
300 pub fn header_bg(mut self, color: Color) -> Self {
301 self.table.header_bg = Some(color);
302 self
303 }
304
305 pub fn position(mut self, x: f64, y: f64) -> Self {
306 self.table.x = x;
307 self.table.y = y;
308 self
309 }
310
311 pub fn width(mut self, w: f64) -> Self {
312 self.table.width = w;
313 self
314 }
315
316 pub fn header_position(mut self, pos: HeaderPosition) -> Self {
317 self.table.header_position = pos;
318 self
319 }
320
321 pub fn build(self) -> TableConfig {
322 let widths = self.table.compute_widths();
323 if self.table.column_widths.is_empty() && widths.is_empty() {
324 let mut t = self.table.clone();
325 t.column_widths = vec![];
326 return t;
327 }
328 self.table
329 }
330}
331
332impl Default for TableBuilder {
333 fn default() -> Self {
334 Self::new()
335 }
336}
337
338pub fn all_border(style: BorderStyle, width: f64, color: Color) -> Border {
339 Border {
340 top: style,
341 bottom: style,
342 left: style,
343 right: style,
344 width,
345 color,
346 }
347}
348
349pub fn grid_border(color: Color) -> Border {
350 all_border(BorderStyle::Solid, 0.5, color)
351}
352
353pub fn outer_border(color: Color) -> Border {
354 Border {
355 top: BorderStyle::Solid,
356 bottom: BorderStyle::Solid,
357 left: BorderStyle::Solid,
358 right: BorderStyle::Solid,
359 width: 1.0,
360 color,
361 }
362}