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 scale_factor: f32,
63 font_scale: f32,
64) -> TableLayout {
65 let cols = params.columns.max(1);
66 let rows = params.rows.max(1);
67 let border = params.border_width;
68 let padding = params.cell_padding;
69 let spacing = params.cell_spacing;
70
71 let total_overhead =
73 border * 2.0 + spacing * (cols as f32 - 1.0).max(0.0) + padding * 2.0 * cols as f32;
74 let content_area = (available_width - total_overhead).max(0.0);
75
76 let column_content_widths =
78 compute_column_widths(¶ms.column_widths, cols, content_area, padding);
79
80 let mut column_xs = Vec::with_capacity(cols);
82 let mut x = border;
83 for (c, &col_w) in column_content_widths.iter().enumerate() {
84 column_xs.push(x + padding);
85 x += padding * 2.0 + col_w;
86 if c < cols - 1 {
87 x += spacing;
88 }
89 }
90 let total_width = x + border;
91
92 let mut cell_layouts = Vec::new();
94 let mut row_heights = vec![0.0f32; rows];
96
97 for cell_params in ¶ms.cells {
98 let r = cell_params.row;
99 let c = cell_params.column;
100 if c >= cols || r >= rows {
101 continue;
102 }
103
104 let cell_width = column_content_widths[c];
105 let mut cell_blocks = Vec::new();
106 let mut cell_height = 0.0f32;
107
108 for block_params in &cell_params.blocks {
109 let block = layout_block(registry, block_params, cell_width, scale_factor, font_scale);
110 cell_height += block.height;
111 cell_blocks.push(block);
112 }
113
114 let mut block_y = 0.0f32;
116 for block in &mut cell_blocks {
117 block.y = block_y;
118 block_y += block.height;
119 }
120
121 row_heights[r] = row_heights[r].max(cell_height);
122
123 cell_layouts.push(CellLayout {
124 row: r,
125 column: c,
126 blocks: cell_blocks,
127 background_color: cell_params.background_color,
128 });
129 }
130
131 let mut row_ys = Vec::with_capacity(rows);
133 let mut y = border;
134 for (r, &row_h) in row_heights.iter().enumerate() {
135 row_ys.push(y + padding);
136 y += padding * 2.0 + row_h;
137 if r < rows - 1 {
138 y += spacing;
139 }
140 }
141 let total_height = y + border;
142
143 TableLayout {
144 table_id: params.table_id,
145 y: 0.0, total_height,
147 total_width,
148 column_xs,
149 column_content_widths,
150 row_ys,
151 row_heights,
152 cell_layouts,
153 border_width: border,
154 cell_padding: padding,
155 }
156}
157
158fn compute_column_widths(
159 specified: &[f32],
160 cols: usize,
161 content_area: f32,
162 _padding: f32,
163) -> Vec<f32> {
164 let default_col_width = if content_area.is_finite() {
168 (content_area / cols as f32).max(1.0)
169 } else {
170 200.0
171 };
172
173 if specified.is_empty() || specified.len() != cols {
174 return vec![default_col_width; cols];
175 }
176
177 let total: f32 = specified.iter().sum();
179 if total <= 0.0 {
180 return vec![default_col_width; cols];
181 }
182
183 specified
184 .iter()
185 .map(|&s| (s / total) * content_area)
186 .collect()
187}
188
189pub fn generate_table_decorations(table: &TableLayout, scroll_offset: f32) -> Vec<DecorationRect> {
191 generate_table_decorations_at(table, 0.0, 0.0, scroll_offset)
192}
193
194pub fn generate_table_decorations_at(
199 table: &TableLayout,
200 offset_x: f32,
201 offset_y: f32,
202 scroll_offset: f32,
203) -> Vec<DecorationRect> {
204 let mut decorations = Vec::new();
205 let table_y = offset_y + table.y - scroll_offset;
206
207 if table.border_width > 0.0 {
209 let bw = table.border_width;
210 let color = [0.6, 0.6, 0.6, 1.0]; decorations.push(DecorationRect {
213 rect: [offset_x, table_y, table.total_width, bw],
214 color,
215 kind: DecorationKind::TableBorder,
216 });
217 decorations.push(DecorationRect {
219 rect: [
220 offset_x,
221 table_y + table.total_height - bw,
222 table.total_width,
223 bw,
224 ],
225 color,
226 kind: DecorationKind::TableBorder,
227 });
228 decorations.push(DecorationRect {
230 rect: [offset_x, table_y, bw, table.total_height],
231 color,
232 kind: DecorationKind::TableBorder,
233 });
234 decorations.push(DecorationRect {
236 rect: [
237 offset_x + table.total_width - bw,
238 table_y,
239 bw,
240 table.total_height,
241 ],
242 color,
243 kind: DecorationKind::TableBorder,
244 });
245
246 for r in 1..table.row_ys.len() {
248 let row_y = table.row_ys[r] - table.cell_padding;
249 decorations.push(DecorationRect {
250 rect: [offset_x, table_y + row_y - bw / 2.0, table.total_width, bw],
251 color,
252 kind: DecorationKind::TableBorder,
253 });
254 }
255
256 for c in 1..table.column_xs.len() {
258 let col_x = table.column_xs[c] - table.cell_padding;
259 decorations.push(DecorationRect {
260 rect: [offset_x + col_x - bw / 2.0, table_y, bw, table.total_height],
261 color,
262 kind: DecorationKind::TableBorder,
263 });
264 }
265 }
266
267 for cell in &table.cell_layouts {
269 if let Some(bg_color) = cell.background_color
270 && cell.row < table.row_ys.len()
271 && cell.column < table.column_xs.len()
272 {
273 let cx = table.column_xs[cell.column] - table.cell_padding;
274 let cy = table.row_ys[cell.row] - table.cell_padding;
275 let cw = table.column_content_widths[cell.column] + table.cell_padding * 2.0;
276 let ch = table.row_heights[cell.row] + table.cell_padding * 2.0;
277 decorations.push(DecorationRect {
278 rect: [offset_x + cx, table_y + cy, cw, ch],
279 color: bg_color,
280 kind: DecorationKind::TableCellBackground,
281 });
282 }
283 }
284
285 decorations
286}