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