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 if specified.is_empty() || specified.len() != cols {
163 let w = content_area / cols as f32;
165 return vec![w; cols];
166 }
167
168 let total: f32 = specified.iter().sum();
170 if total <= 0.0 {
171 let w = content_area / cols as f32;
172 return vec![w; cols];
173 }
174
175 specified
176 .iter()
177 .map(|&s| (s / total) * content_area)
178 .collect()
179}
180
181pub fn generate_table_decorations(table: &TableLayout, scroll_offset: f32) -> Vec<DecorationRect> {
183 let mut decorations = Vec::new();
184 let table_y = table.y - scroll_offset;
185
186 if table.border_width > 0.0 {
188 let bw = table.border_width;
189 let color = [0.6, 0.6, 0.6, 1.0]; decorations.push(DecorationRect {
192 rect: [0.0, table_y, table.total_width, bw],
193 color,
194 kind: DecorationKind::TableBorder,
195 });
196 decorations.push(DecorationRect {
198 rect: [
199 0.0,
200 table_y + table.total_height - bw,
201 table.total_width,
202 bw,
203 ],
204 color,
205 kind: DecorationKind::TableBorder,
206 });
207 decorations.push(DecorationRect {
209 rect: [0.0, table_y, bw, table.total_height],
210 color,
211 kind: DecorationKind::TableBorder,
212 });
213 decorations.push(DecorationRect {
215 rect: [table.total_width - bw, table_y, bw, table.total_height],
216 color,
217 kind: DecorationKind::TableBorder,
218 });
219
220 for r in 1..table.row_ys.len() {
222 let row_y = table.row_ys[r] - table.cell_padding;
223 decorations.push(DecorationRect {
224 rect: [0.0, table_y + row_y - bw / 2.0, table.total_width, bw],
225 color,
226 kind: DecorationKind::TableBorder,
227 });
228 }
229
230 for c in 1..table.column_xs.len() {
232 let col_x = table.column_xs[c] - table.cell_padding;
233 decorations.push(DecorationRect {
234 rect: [col_x - bw / 2.0, table_y, bw, table.total_height],
235 color,
236 kind: DecorationKind::TableBorder,
237 });
238 }
239 }
240
241 for cell in &table.cell_layouts {
243 if let Some(bg_color) = cell.background_color
244 && cell.row < table.row_ys.len()
245 && cell.column < table.column_xs.len()
246 {
247 let cx = table.column_xs[cell.column] - table.cell_padding;
248 let cy = table.row_ys[cell.row] - table.cell_padding;
249 let cw = table.column_content_widths[cell.column] + table.cell_padding * 2.0;
250 let ch = table.row_heights[cell.row] + table.cell_padding * 2.0;
251 decorations.push(DecorationRect {
252 rect: [cx, table_y + cy, cw, ch],
253 color: bg_color,
254 kind: DecorationKind::TableCellBackground,
255 });
256 }
257 }
258
259 decorations
260}