Skip to main content

paperforge_layout/
table.rs

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}