1use alloc::collections::BTreeMap;
30use alloc::vec::Vec;
31
32use super::consts::*;
33use super::Ui;
34
35#[derive(Clone, Default)]
37pub(crate) struct Mem {
38 colw: Vec<i32>,
39 rowh: Vec<i32>,
40}
41
42#[derive(Clone, Default)]
44pub(crate) struct Run {
45 id: u32,
46 flags: u32,
47 gap: i32,
48 cols: Vec<i32>,
50 colx: Vec<i32>,
52 colw: Vec<i32>,
53 rows: Vec<i32>,
55 rowh: Vec<i32>,
56 rowy: Vec<i32>,
57 pos: [i32; 2],
59 len: [i32; 2],
60 col: usize,
62 row: usize,
63 busy: Vec<usize>,
65 mcol: Vec<i32>,
67 mrow: Vec<i32>,
68 spans_c: Vec<(usize, usize, i32)>,
69 spans_r: Vec<(usize, usize, i32)>,
70}
71
72impl Run {
73 fn next_cell(&self, span: (u16, u16), at: Option<(u16, u16)>) -> (usize, usize) {
76 let ncol = self.colw.len();
77 let cs = (span.0 as usize).min(ncol);
78 let (mut col, mut row) = (self.col, self.row);
79 if let Some((c, r)) = at {
80 col = (c as usize).min(ncol - 1);
81 row = r as usize;
82 }
83 loop {
84 if col + cs > ncol {
85 col = 0;
86 row += 1;
87 continue;
88 }
89 if (col..col + cs).any(|c| self.busy[c] > row) {
90 col += 1;
91 continue;
92 }
93 return (col, row);
94 }
95 }
96}
97
98#[derive(Default)]
100pub(crate) struct W {
101 pub(crate) runs: Vec<Run>,
102 pub(crate) mem: BTreeMap<u32, Mem>,
103 pub(crate) span: (u16, u16),
105 pub(crate) cell: Option<(u16, u16)>,
106 pub(crate) align: (u8, u8),
107 pub(crate) last: (u16, [u16; 4]),
109}
110
111fn h_of(flags: u32) -> u8 {
113 match (flags >> 1) & 3 {
114 1 => AL_START as u8,
115 2 => AL_CENTER as u8,
116 3 => AL_END as u8,
117 _ => AL_STRETCH as u8,
118 }
119}
120fn v_of(flags: u32) -> u8 {
121 match (flags >> 3) & 3 {
122 1 => AL_START as u8,
123 2 => AL_END as u8,
124 3 => AL_STRETCH as u8,
125 _ => AL_CENTER as u8,
126 }
127}
128
129fn sizes(specs: &[i32], nat: &[i32], avail: i32, gap: i32, dpi: i32) -> Vec<i32> {
131 let n = specs.len();
132 let mut out: Vec<i32> = alloc::vec![0; n];
133 let (mut used, mut wsum) = (gap * (n as i32 - 1).max(0), 0i32);
134 for (k, &s) in specs.iter().enumerate() {
135 if s > 0 {
136 out[k] = super::layout::dp_at(s, dpi);
137 } else if s == 0 {
138 out[k] = nat.get(k).copied().unwrap_or(0);
139 } else {
140 wsum += -s;
141 continue;
142 }
143 used += out[k];
144 }
145 if wsum > 0 {
146 let spare = (avail - used).max(0);
147 let mut left = spare;
148 let last = specs.iter().rposition(|&s| s < 0).unwrap_or(0);
149 for (k, &s) in specs.iter().enumerate() {
150 if s < 0 {
151 out[k] = if k == last { left } else { spare * -s / wsum };
152 left -= out[k];
153 }
154 }
155 }
156 out
157}
158
159impl Ui {
160 #[cfg_attr(jfgui_dev, track_caller)]
162 pub fn gridbox_begin(&mut self, id: u32, cols: &[i32], flags: u32, gap: i32) {
163 let _src = self.src_scope();
164 self.box_begin(id, BOX_V | (flags & !0x3F), 0);
166 let id = self.box_id();
167 let b = *self.wp.lay.boxes.last().unwrap_or(&Default::default());
168 let gap = self.dp_space(gap); let dpi = self.wp.rt.dpi;
170 let mem = self.wp.lay.grid.mem.get(&id).cloned().unwrap_or_default();
171 let cols = if cols.is_empty() { &[0][..] } else { cols };
172 let colw = sizes(cols, &mem.colw, b.len[0], gap, dpi);
173 let mut colx = Vec::with_capacity(colw.len());
174 let mut x = b.pos[0];
175 for w in &colw {
176 colx.push(x);
177 x += w + gap;
178 }
179 let n = cols.len();
180 let run = Run {
181 id,
182 flags,
183 gap,
184 cols: cols.to_vec(),
185 colx,
186 colw,
187 rows: Vec::new(),
188 rowh: mem.rowh.clone(),
189 rowy: Vec::new(),
190 pos: b.pos,
191 len: b.len,
192 busy: alloc::vec![0; n],
193 mcol: alloc::vec![0; n],
194 mrow: Vec::new(),
195 ..Run::default()
196 };
197 self.wp.lay.grid.runs.push(run);
198 let k = self.wp.lay.grid.runs.len() as u16;
199 if let Some(top) = self.wp.lay.boxes.last_mut() {
200 top.grid = k;
201 }
202 self.grid_layout_rows();
203 }
204
205 pub fn gridbox_rows(&mut self, rows: &[i32]) {
207 if let Some(r) = self.wp.lay.grid.runs.last_mut() {
208 r.rows = rows.to_vec();
209 }
210 self.grid_layout_rows();
211 }
212
213 fn grid_layout_rows(&mut self) {
215 let dpi = self.wp.rt.dpi;
216 let mem = {
217 let Some(r) = self.wp.lay.grid.runs.last() else { return };
218 self.wp.lay.grid.mem.get(&r.id).cloned().unwrap_or_default()
219 };
220 let Some(r) = self.wp.lay.grid.runs.last_mut() else { return };
221 let n = r.rows.len().max(mem.rowh.len());
222 let specs: Vec<i32> = (0..n).map(|k| r.rows.get(k).copied().unwrap_or(0)).collect();
223 r.rowh = sizes(&specs, &mem.rowh, r.len[1], r.gap, dpi);
224 r.rowy.clear();
225 let mut y = r.pos[1];
226 for h in &r.rowh {
227 r.rowy.push(y);
228 y += h + r.gap;
229 }
230 }
231
232 pub fn next_span(&mut self, cols: u32, rows: u32) {
234 self.wp.lay.grid.span = (cols.clamp(1, 1000) as u16, rows.clamp(1, 1000) as u16);
235 }
236
237 pub fn next_cell(&mut self, col: u32, row: u32) {
239 self.wp.lay.grid.cell = Some((col.min(1000) as u16, row.min(100_000) as u16));
240 }
241
242 pub fn next_align(&mut self, h: u32, v: u32) {
244 self.wp.lay.grid.align = (h.min(4) as u8, v.min(4) as u8);
245 }
246
247 pub(crate) fn grid_avail_width(&self, k: usize, iw: i32) -> (i32, i32) {
251 let l = &self.wp.lay;
252 let Some(r) = l.grid.runs.get(k - 1) else { return (iw, 0) };
253 let span = (l.grid.span.0.max(1), l.grid.span.1.max(1));
254 let (c, _) = r.next_cell(span, l.grid.cell);
255 let e = c + (span.0 as usize).min(r.colw.len()) - 1;
256 let cw = r.colx[e] + r.colw[e] - r.colx[c];
257 let ha = if l.grid.align.0 != 0 { l.grid.align.0 } else { h_of(r.flags) };
258 if l.next_w > 0 {
259 return (self.dp(l.next_w).min(cw), 1);
260 }
261 if l.next_w < 0 || ha == AL_STRETCH as u8 {
262 return (cw, 1);
263 }
264 if cw <= 0 {
265 return (iw, 0);
266 }
267 (cw.min(iw), 0)
268 }
269
270 pub(crate) fn grid_place(&mut self, k: usize, iw: i32, ih: i32) -> [i32; 4] {
272 let dpi = self.wp.rt.dpi;
273 let l = &mut self.wp.lay;
274 let (rw, rh) = (l.next_w, l.next_h);
275 l.next_w = 0;
276 l.next_h = 0;
277 l.place_auto = 0;
278 l.place_main = 0;
279 let span = core::mem::replace(&mut l.grid.span, (1, 1));
280 let span = (span.0.max(1), span.1.max(1));
281 let at = l.grid.cell.take();
282 let al = core::mem::take(&mut l.grid.align);
283 let r = &mut l.grid.runs[k - 1];
284 let cs = (span.0 as usize).min(r.colw.len());
285 let rs = span.1 as usize;
286 let (c, row) = r.next_cell(span, at);
288 r.row = row;
289 let nat_w = if rw > 0 { super::layout::dp_at(rw, dpi) } else { iw };
291 let nat_h = if rh > 0 { super::layout::dp_at(rh, dpi) } else { ih };
292 while r.rowh.len() < row + rs {
294 let y = match (r.rowy.last(), r.rowh.last()) {
295 (Some(y), Some(h)) => y + h + r.gap,
296 _ => r.pos[1],
297 };
298 r.rowy.push(y);
299 r.rowh.push(if rs == 1 { nat_h } else { 0 });
300 }
301 if r.mrow.len() < row + rs {
302 r.mrow.resize(row + rs, 0);
303 }
304 if cs == 1 {
306 r.mcol[c] = r.mcol[c].max(nat_w);
307 } else {
308 r.spans_c.push((c, cs, nat_w));
309 }
310 if rs == 1 {
311 r.mrow[row] = r.mrow[row].max(nat_h);
312 } else {
313 r.spans_r.push((row, rs, nat_h));
314 }
315 let cx = r.colx[c];
317 let cw = r.colx[c + cs - 1] + r.colw[c + cs - 1] - cx;
318 let cy = r.rowy[row];
319 let ch = r.rowy[row + rs - 1] + r.rowh[row + rs - 1] - cy;
320 let ha = if al.0 != 0 { al.0 } else { h_of(r.flags) };
322 let va = if al.1 != 0 { al.1 } else { v_of(r.flags) };
323 let place = |a: u8, start: i32, size: i32, want: i32, fill: bool| -> (i32, i32) {
324 if fill || (a == AL_STRETCH as u8 && want <= 0) {
325 return (start, size);
326 }
327 let w = if a == AL_STRETCH as u8 { want.min(size) } else { want.min(size).max(0) };
328 match a {
329 x if x == AL_CENTER as u8 => (start + (size - w) / 2, w),
330 x if x == AL_END as u8 => (start + size - w, w),
331 _ => (start, w),
332 }
333 };
334 let (x, w) = place(ha, cx, cw, if rw > 0 { nat_w } else if ha == AL_STRETCH as u8 { 0 } else { nat_w }, rw < 0);
335 let (y, h) = place(va, cy, ch, if rh > 0 { nat_h } else if va == AL_STRETCH as u8 { 0 } else { nat_h }, rh < 0);
336 if rs > 1 {
338 for b in &mut r.busy[c..c + cs] {
339 *b = row + rs;
340 }
341 }
342 r.col = c + cs;
343 l.grid.last = (k as u16, [c as u16, row as u16, cs as u16, rs as u16]);
344 [x, y, w, h]
345 }
346
347 pub(crate) fn grid_child_measured(&mut self, k: usize, cell: [u16; 4], w: i32, h: i32) {
349 let Some(r) = self.wp.lay.grid.runs.get_mut(k - 1) else { return };
350 let [c, row, cs, rs] = cell.map(|v| v as usize);
351 if cs == 1 && c < r.mcol.len() {
352 r.mcol[c] = r.mcol[c].max(w);
353 } else if cs > 1 {
354 r.spans_c.push((c, cs, w));
355 }
356 if rs == 1 && row < r.mrow.len() {
357 r.mrow[row] = r.mrow[row].max(h);
358 } else if rs > 1 {
359 r.spans_r.push((row, rs, h));
360 }
361 }
362
363 #[cfg_attr(jfgui_dev, track_caller)]
365 pub fn gridbox_end(&mut self) {
366 let _src = self.src_scope();
367 let Some(mut r) = self.wp.lay.grid.runs.pop() else {
368 self.warn("gridbox_end without gridbox_begin");
369 return;
370 };
371 let fit = |m: &mut Vec<i32>, specs: &dyn Fn(usize) -> i32, spans: &[(usize, usize, i32)], gap: i32| {
373 for &(s, n, want) in spans {
374 if s + n > m.len() {
375 m.resize(s + n, 0);
376 }
377 let have: i32 = m[s..s + n].iter().sum::<i32>() + gap * (n as i32 - 1);
378 if want > have {
379 if let Some(k) = (s..s + n).rev().find(|&k| specs(k) == 0) {
380 m[k] += want - have;
381 }
382 }
383 }
384 };
385 let mut mcol = core::mem::take(&mut r.mcol);
386 let mut mrow = core::mem::take(&mut r.mrow);
387 let (cols_spec, rows_spec) = (r.cols.clone(), r.rows.clone());
388 fit(&mut mcol, &|k| cols_spec.get(k).copied().unwrap_or(0), &r.spans_c, r.gap);
389 fit(&mut mrow, &|k| rows_spec.get(k).copied().unwrap_or(0), &r.spans_r, r.gap);
390 let changed = self.wp.lay.grid.mem.get(&r.id).is_none_or(|m| m.colw != mcol || m.rowh != mrow);
391 if changed {
392 self.repass = true;
393 }
394 let dpi = self.wp.rt.dpi;
396 let gap = r.gap;
397 let colw: i32 = (0..cols_spec.len())
399 .map(|k| match cols_spec[k] {
400 s if s > 0 => super::layout::dp_at(s, dpi),
401 _ => mcol.get(k).copied().unwrap_or(0),
402 })
403 .sum::<i32>()
404 + gap * (cols_spec.len() as i32 - 1).max(0);
405 let nrow = mrow.len().max(rows_spec.len());
406 let rowh: i32 = (0..nrow)
407 .map(|k| match rows_spec.get(k).copied().unwrap_or(0) {
408 s if s > 0 => super::layout::dp_at(s, dpi),
409 _ => mrow.get(k).copied().unwrap_or(0),
410 })
411 .sum::<i32>()
412 + gap * (nrow as i32 - 1).max(0);
413 self.wp.lay.grid.mem.insert(r.id, Mem { colw: mcol, rowh: mrow });
414 if let Some(top) = self.wp.lay.boxes.last_mut() {
415 top.grid = 0;
416 top.fixed = rowh; top.cross = colw;
418 top.wsum = 0;
419 }
420 self.box_end();
421 }
422}