1use crate::font::registry::FontRegistry;
2use crate::layout::block::{BlockLayout, BlockLayoutParams, layout_block};
3use crate::types::{DecorationKind, DecorationRect};
4
5pub struct TableLayoutParams {
7 pub table_id: usize,
8 pub rows: usize,
9 pub columns: usize,
10 pub column_widths: Vec<f32>,
12 pub border_width: f32,
13 pub cell_spacing: f32,
14 pub cell_padding: f32,
15 pub cells: Vec<CellLayoutParams>,
17}
18
19pub struct CellLayoutParams {
21 pub row: usize,
22 pub column: usize,
23 pub blocks: Vec<BlockLayoutParams>,
24 pub background_color: Option<[f32; 4]>,
25}
26
27pub struct TableLayout {
29 pub table_id: usize,
30 pub y: f32,
32 pub total_height: f32,
34 pub total_width: f32,
36 pub column_xs: Vec<f32>,
38 pub column_content_widths: Vec<f32>,
40 pub row_ys: Vec<f32>,
42 pub row_heights: Vec<f32>,
44 pub cell_layouts: Vec<CellLayout>,
46 pub border_width: f32,
47 pub cell_padding: f32,
48}
49
50pub struct CellLayout {
51 pub row: usize,
52 pub column: usize,
53 pub blocks: Vec<BlockLayout>,
54 pub background_color: Option<[f32; 4]>,
55}
56
57pub fn layout_table(
59 registry: &FontRegistry,
60 params: &TableLayoutParams,
61 available_width: f32,
62) -> TableLayout {
63 let cols = params.columns.max(1);
64 let rows = params.rows.max(1);
65 let border = params.border_width;
66 let padding = params.cell_padding;
67 let spacing = params.cell_spacing;
68
69 let total_overhead =
71 border * 2.0 + spacing * (cols as f32 - 1.0).max(0.0) + padding * 2.0 * cols as f32;
72 let content_area = (available_width - total_overhead).max(0.0);
73
74 let column_content_widths =
76 compute_column_widths(¶ms.column_widths, cols, content_area, padding);
77
78 let mut column_xs = Vec::with_capacity(cols);
80 let mut x = border;
81 for (c, &col_w) in column_content_widths.iter().enumerate() {
82 column_xs.push(x + padding);
83 x += padding * 2.0 + col_w;
84 if c < cols - 1 {
85 x += spacing;
86 }
87 }
88 let total_width = x + border;
89
90 let mut cell_layouts = Vec::new();
92 let mut row_heights = vec![0.0f32; rows];
94
95 for cell_params in ¶ms.cells {
96 let r = cell_params.row;
97 let c = cell_params.column;
98 if c >= cols || r >= rows {
99 continue;
100 }
101
102 let cell_width = column_content_widths[c];
103 let mut cell_blocks = Vec::new();
104 let mut cell_height = 0.0f32;
105
106 for block_params in &cell_params.blocks {
107 let block = layout_block(registry, block_params, cell_width);
108 cell_height += block.height;
109 cell_blocks.push(block);
110 }
111
112 let mut block_y = 0.0f32;
114 for block in &mut cell_blocks {
115 block.y = block_y;
116 block_y += block.height;
117 }
118
119 row_heights[r] = row_heights[r].max(cell_height);
120
121 cell_layouts.push(CellLayout {
122 row: r,
123 column: c,
124 blocks: cell_blocks,
125 background_color: cell_params.background_color,
126 });
127 }
128
129 let mut row_ys = Vec::with_capacity(rows);
131 let mut y = border;
132 for (r, &row_h) in row_heights.iter().enumerate() {
133 row_ys.push(y + padding);
134 y += padding * 2.0 + row_h;
135 if r < rows - 1 {
136 y += spacing;
137 }
138 }
139 let total_height = y + border;
140
141 TableLayout {
142 table_id: params.table_id,
143 y: 0.0, total_height,
145 total_width,
146 column_xs,
147 column_content_widths,
148 row_ys,
149 row_heights,
150 cell_layouts,
151 border_width: border,
152 cell_padding: padding,
153 }
154}
155
156fn compute_column_widths(
157 specified: &[f32],
158 cols: usize,
159 content_area: f32,
160 _padding: f32,
161) -> Vec<f32> {
162 let default_col_width = if content_area.is_finite() {
166 (content_area / cols as f32).max(1.0)
167 } else {
168 200.0
169 };
170
171 if specified.is_empty() || specified.len() != cols {
172 return vec![default_col_width; cols];
173 }
174
175 let total: f32 = specified.iter().sum();
177 if total <= 0.0 {
178 return vec![default_col_width; cols];
179 }
180
181 specified
182 .iter()
183 .map(|&s| (s / total) * content_area)
184 .collect()
185}
186
187pub fn generate_table_decorations(table: &TableLayout, scroll_offset: f32) -> Vec<DecorationRect> {
189 let mut decorations = Vec::new();
190 let table_y = table.y - scroll_offset;
191
192 if table.border_width > 0.0 {
194 let bw = table.border_width;
195 let color = [0.6, 0.6, 0.6, 1.0]; decorations.push(DecorationRect {
198 rect: [0.0, table_y, table.total_width, bw],
199 color,
200 kind: DecorationKind::TableBorder,
201 });
202 decorations.push(DecorationRect {
204 rect: [
205 0.0,
206 table_y + table.total_height - bw,
207 table.total_width,
208 bw,
209 ],
210 color,
211 kind: DecorationKind::TableBorder,
212 });
213 decorations.push(DecorationRect {
215 rect: [0.0, table_y, bw, table.total_height],
216 color,
217 kind: DecorationKind::TableBorder,
218 });
219 decorations.push(DecorationRect {
221 rect: [table.total_width - bw, table_y, bw, table.total_height],
222 color,
223 kind: DecorationKind::TableBorder,
224 });
225
226 for r in 1..table.row_ys.len() {
228 let row_y = table.row_ys[r] - table.cell_padding;
229 decorations.push(DecorationRect {
230 rect: [0.0, table_y + row_y - bw / 2.0, table.total_width, bw],
231 color,
232 kind: DecorationKind::TableBorder,
233 });
234 }
235
236 for c in 1..table.column_xs.len() {
238 let col_x = table.column_xs[c] - table.cell_padding;
239 decorations.push(DecorationRect {
240 rect: [col_x - bw / 2.0, table_y, bw, table.total_height],
241 color,
242 kind: DecorationKind::TableBorder,
243 });
244 }
245 }
246
247 for cell in &table.cell_layouts {
249 if let Some(bg_color) = cell.background_color
250 && cell.row < table.row_ys.len()
251 && cell.column < table.column_xs.len()
252 {
253 let cx = table.column_xs[cell.column] - table.cell_padding;
254 let cy = table.row_ys[cell.row] - table.cell_padding;
255 let cw = table.column_content_widths[cell.column] + table.cell_padding * 2.0;
256 let ch = table.row_heights[cell.row] + table.cell_padding * 2.0;
257 decorations.push(DecorationRect {
258 rect: [cx, table_y + cy, cw, ch],
259 color: bg_color,
260 kind: DecorationKind::TableCellBackground,
261 });
262 }
263 }
264
265 decorations
266}