1use alloc::collections::BTreeMap;
34use alloc::vec::Vec;
35
36use super::consts::*;
37use super::{Sp, Ui};
38
39pub(crate) const BOX_STACK_MAX: usize = 48;
41
42#[derive(Clone, Copy, Default)]
44pub(crate) struct BoxFrame {
45 pub(crate) id: u32,
46 pub(crate) flags: u32,
47 pub(crate) pos: [i32; 2],
49 pub(crate) len: [i32; 2],
50 pub(crate) cur: i32,
52 pub(crate) gap: i32,
53 pub(crate) fixed: i32,
55 pub(crate) wsum: i32,
56 pub(crate) fixedp: i32,
57 pub(crate) wsump: i32,
58 pub(crate) n: i32,
60 pub(crate) cross: i32,
62 pub(crate) pad: i32,
63 pub(crate) auto: u32,
66 pub(crate) pmain: i32,
68 pub(crate) nbox: u32,
70 pub(crate) grid: u16,
73 pub(crate) gcell: (u16, [u16; 4]),
74}
75
76#[derive(Clone, Copy, Default, PartialEq)]
79pub(crate) struct Row {
80 pub(crate) h: i32,
81 pub(crate) w: i32,
82}
83
84#[derive(Default)]
87pub(crate) struct Wrap {
88 rows: Vec<Row>,
89 top: i32,
90 n: i32,
91 prev: Vec<Row>,
92}
93
94fn wraps(flags: u32) -> bool {
96 flags & (BOX_WRAP | BOX_V) == BOX_WRAP
97}
98
99fn row_start(flags: u32, len: i32, prev: Option<&Row>) -> i32 {
102 let spare = prev.map_or(0, |r| len.wrapping_sub(r.w));
103 if spare <= 0 {
104 0
105 } else if flags & BOX_CENTER != 0 {
106 spare >> 1
107 } else if flags & BOX_END != 0 {
108 spare
109 } else {
110 0
111 }
112}
113
114#[derive(Default)]
116pub(crate) struct W {
117 pub(crate) boxes: Vec<BoxFrame>,
118 pub(crate) next_w: i32,
119 pub(crate) next_h: i32,
120 pub(crate) next_rx: i32,
121 pub(crate) next_ry: i32,
122 pub(crate) next_rw: i32,
123 pub(crate) next_rh: i32,
124 pub(crate) next_rect_set: u32,
126 pub(crate) next_flags: u32,
127 pub(crate) next_icon: u32,
128 pub(crate) place_auto: u32,
129 pub(crate) place_main: i32,
130 pub(crate) placing_box: bool,
133 pub(crate) root_id: u32,
135 pub(crate) grid: super::grid::W,
137 pub(crate) wraps: Vec<Wrap>,
139 pub(crate) wrap_rows: BTreeMap<u32, Vec<Row>>,
140}
141
142impl Ui {
143 fn top(&mut self) -> &mut BoxFrame {
146 let b = &mut self.wp.lay.boxes;
147 if b.is_empty() {
148 b.push(BoxFrame::default());
149 }
150 let n = b.len();
151 &mut b[n - 1]
152 }
153
154 pub(crate) fn layout_root(&mut self, x: i32, y: i32, w: i32, h: i32) {
156 let id = self.wp.lay.root_id;
157 let s = *self.slot(id);
158 let l = &mut self.wp.lay;
159 l.boxes.clear();
160 l.boxes.push(BoxFrame {
161 id,
162 flags: BOX_V,
163 pos: [x, y],
164 len: [w, h],
165 cur: y,
166 fixedp: s.fixed,
167 wsump: s.wsum,
168 ..BoxFrame::default()
169 });
170 l.next_w = 0;
171 l.next_h = 0;
172 l.next_rect_set = 0;
173 l.next_flags = 0;
174 l.next_icon = 0;
175 l.grid.runs.clear();
176 l.grid.span = (1, 1);
177 l.grid.cell = None;
178 l.grid.align = (0, 0);
179 l.wraps.clear();
180 }
181
182 pub(crate) fn layout_end_root(&mut self) {
184 while self.wp.lay.boxes.len() > 1 {
185 self.warn("ui_box_begin without ui_box_end");
186 self.box_end();
187 }
188 let b = self.wp.lay.boxes.first().copied().unwrap_or_default();
189 let id = self.wp.lay.root_id;
190 let s = self.slot(id);
191 let changed = b.fixed != s.fixed || b.wsum != s.wsum;
192 s.fixed = b.fixed;
193 s.wsum = b.wsum;
194 if changed {
195 self.repass = true;
196 }
197 self.wp.lay.boxes.clear();
198 }
199
200 pub fn next_size(&mut self, w: i32, h: i32) {
202 self.wp.lay.next_w = w;
203 self.wp.lay.next_h = h;
204 }
205
206 pub fn next_rect(&mut self, x: i32, y: i32, w: i32, h: i32) {
208 let l = &mut self.wp.lay;
209 l.next_rx = x;
210 l.next_ry = y;
211 l.next_rw = w;
212 l.next_rh = h;
213 l.next_rect_set = 1;
214 }
215
216 pub fn next_rect_px(&mut self, x: i32, y: i32, w: i32, h: i32) {
219 let l = &mut self.wp.lay;
220 l.next_rx = x;
221 l.next_ry = y;
222 l.next_rw = w;
223 l.next_rh = h;
224 l.next_rect_set = 2;
225 }
226
227 pub fn box_offset(&mut self, dx: i32, dy: i32) {
229 let b = self.top();
230 b.pos[0] = b.pos[0].wrapping_sub(dx);
231 b.pos[1] = b.pos[1].wrapping_sub(dy);
232 if b.flags & BOX_V != 0 {
233 b.cur = b.cur.wrapping_sub(dy);
234 } else {
235 b.cur = b.cur.wrapping_sub(dx);
236 }
237 }
238
239 pub fn next_flags(&mut self, f: u32) {
241 self.wp.lay.next_flags |= f;
242 }
243
244 pub fn take_flags(&mut self) -> u32 {
246 core::mem::take(&mut self.wp.lay.next_flags)
247 }
248
249 pub fn next_icon(&mut self, cp: u32) {
251 self.wp.lay.next_icon = cp;
252 }
253
254 pub fn take_icon(&mut self) -> u32 {
256 core::mem::take(&mut self.wp.lay.next_icon)
257 }
258
259 pub fn last_rect(&self) -> [i32; 4] {
261 self.wp.ctx.last_rect
262 }
263
264 pub fn box_id(&self) -> u32 {
266 self.wp.lay.boxes.last().map_or(0, |b| b.id)
267 }
268
269 pub fn box_rect(&self) -> [i32; 4] {
271 let b = self.wp.lay.boxes.last().copied().unwrap_or_default();
272 [b.pos[0], b.pos[1], b.len[0], b.len[1]]
273 }
274
275 pub fn box_flags(&self) -> u32 {
277 self.wp.lay.boxes.last().map_or(0, |b| b.flags)
278 }
279
280 pub fn place(&mut self, iw: i32, ih: i32) -> [i32; 4] {
282 let dpi = self.wp.rt.dpi;
283 let dp = |v: i32| dp_at(v, dpi);
284 let mut repass = false;
285 self.top();
286 let gk = self.wp.lay.boxes.last().map_or(0, |b| b.grid);
288 if gk != 0 && self.wp.lay.next_rect_set == 0 {
289 let r = self.grid_place(gk as usize, iw, ih);
290 self.wp.ctx.last_rect = r;
291 return r;
292 }
293 let l = &mut self.wp.lay;
294 let r = if l.next_rect_set == 0 {
295 let n = l.boxes.len();
296 let b = &mut l.boxes[n - 1];
297 let a = (b.flags & BOX_V) as usize; let c = a ^ 1; let mut wr = if wraps(b.flags) { l.wraps.last_mut() } else { None };
301 let (mut rmain, mut rcross) = (l.next_w, l.next_h);
303 let (nat_main, mut nat_cross) = if a == 1 { (ih, iw) } else { (iw, ih) };
304 if a == 1 {
305 core::mem::swap(&mut rmain, &mut rcross);
306 }
307 let aligned = b.flags & (BOX_XCENTER | BOX_XSTART | BOX_XEND) != 0;
309 let mut auto = 0;
310 if rmain == 0 {
311 auto |= 1;
312 }
313 if rcross == 0 && aligned {
314 auto |= 2;
315 } else if rcross <= 0 {
316 auto |= 4;
318 }
319 l.place_auto = auto;
320 let mut weight = 0;
323 let main = if rmain == 0 {
324 nat_main
325 } else if rmain < 0 {
326 weight = rmain.wrapping_neg();
327 let spare = (b.len[a].wrapping_sub(b.fixedp)).max(0);
328 if b.wsump == 0 {
329 repass = true;
330 if wr.is_some() { nat_main } else { 0 }
331 } else if wr.is_some() && spare == 0 {
332 nat_main
333 } else {
334 spare.wrapping_mul(weight) / b.wsump
335 }
336 } else {
337 dp(rmain)
338 };
339 if let Some(w) = wr.as_deref_mut() {
342 let used = w.rows.last().map_or(0, |r| r.w);
343 if w.n != 0 && used.wrapping_add(b.gap).wrapping_add(main) > b.len[a] {
344 let h = w.rows.last().map_or(0, |r| r.h);
345 w.top = w.top.wrapping_add(h).wrapping_add(b.gap);
346 w.rows.push(Row::default());
347 w.n = 0;
348 b.cur = b.pos[a].wrapping_add(row_start(b.flags, b.len[a], w.prev.get(w.rows.len() - 1)));
349 }
350 }
351 let (start, room) = match wr.as_deref() {
354 Some(w) => (b.pos[c].wrapping_add(w.top), w.prev.get(w.rows.len() - 1).map_or(0, |p| p.h)),
355 None => (b.pos[c], b.len[c]),
356 };
357 let cross = if rcross > 0 {
359 nat_cross = dp(rcross); nat_cross
361 } else if (rcross == 0 && aligned) || (wr.is_some() && room == 0) {
362 nat_cross
363 } else {
364 room
365 };
366 let gap = match wr.as_deref() {
369 Some(w) if w.n == 0 => 0,
370 None if b.n == 0 => 0,
371 _ => b.gap,
372 };
373 if b.n != 0 {
374 b.fixed = b.fixed.wrapping_add(b.gap);
375 }
376 b.cur = b.cur.wrapping_add(gap);
377 b.n += 1;
378 l.place_main = main;
379 if weight == 0 {
380 b.fixed = b.fixed.wrapping_add(main);
381 } else {
382 b.wsum = b.wsum.wrapping_add(weight);
383 }
384 let counts = !(l.placing_box && auto & 6 != 0);
385 if nat_cross > b.cross && counts {
386 b.cross = nat_cross;
387 }
388 if let Some(w) = wr.as_deref_mut() {
389 w.n += 1;
390 if let Some(row) = w.rows.last_mut() {
391 row.w = row.w.wrapping_add(gap).wrapping_add(main);
392 if nat_cross > row.h && counts {
393 row.h = nat_cross;
394 }
395 }
396 }
397 let mut r = [0i32; 4];
399 r[a] = b.cur;
400 r[2 + a] = main;
401 b.cur = b.cur.wrapping_add(main);
402 let mut spare = room.wrapping_sub(cross);
403 if wr.is_some() {
404 spare = spare.max(0); }
406 let mut cp = start;
407 if b.flags & BOX_XCENTER != 0 {
408 cp = cp.wrapping_add(spare >> 1);
409 } else if b.flags & BOX_XEND != 0 {
410 cp = cp.wrapping_add(spare);
411 }
412 r[c] = cp;
413 r[2 + c] = cross;
414 r
415 } else if l.next_rect_set == 2 {
416 l.next_rect_set = 0;
418 l.place_auto = 0;
419 [
420 l.next_rx,
421 l.next_ry,
422 if l.next_rw != 0 { l.next_rw } else { iw },
423 if l.next_rh != 0 { l.next_rh } else { ih },
424 ]
425 } else {
426 l.next_rect_set = 0;
428 l.place_auto = 0;
429 let n = l.boxes.len();
430 let b = l.boxes[n - 1];
431 [
432 dp(l.next_rx).wrapping_add(b.pos[0]),
433 dp(l.next_ry).wrapping_add(b.pos[1]),
434 if l.next_rw != 0 { dp(l.next_rw) } else { iw },
435 if l.next_rh != 0 { dp(l.next_rh) } else { ih },
436 ]
437 };
438 l.next_w = 0;
439 l.next_h = 0;
440 self.wp.ctx.last_rect = r;
441 if repass {
442 self.repass = true;
443 }
444 r
445 }
446
447 pub fn avail_width(&self, iw: i32) -> (i32, i32) {
451 let l = &self.wp.lay;
452 if l.next_rect_set != 0 {
453 if l.next_rw == 0 {
454 return (iw, 0);
455 }
456 return (if l.next_rect_set == 2 { l.next_rw } else { self.dp(l.next_rw) }, 1);
457 }
458 let b = l.boxes.last().copied().unwrap_or_default();
459 if b.grid != 0 {
461 return self.grid_avail_width(b.grid as usize, iw);
462 }
463 let w = l.next_w;
464 if b.flags & BOX_V != 0 {
465 if w > 0 {
467 return (self.dp(w), 1);
468 }
469 if w < 0 || b.flags & (BOX_XCENTER | BOX_XSTART | BOX_XEND) == 0 {
470 return (b.len[0], 1);
471 }
472 if b.len[0] <= 0 {
474 return (iw, 0);
475 }
476 return (b.len[0].min(iw), 0);
477 }
478 if w > 0 {
479 return (self.dp(w), 1);
480 }
481 let wrap = wraps(b.flags);
482 let spare = b.len[0].wrapping_sub(b.fixedp).max(0);
483 if w < 0 && !(wrap && (b.wsump == 0 || spare == 0)) {
484 if b.wsump == 0 {
486 return (0, 1);
487 }
488 return (spare.wrapping_mul(w.wrapping_neg()) / b.wsump, 1);
489 }
490 if wrap {
491 if b.len[0] <= 0 {
494 return (iw, 0);
495 }
496 return (b.len[0].min(iw), 0);
497 }
498 let mut rest = b.pos[0].wrapping_add(b.len[0]).wrapping_sub(b.cur);
500 if b.n != 0 {
501 rest = rest.wrapping_sub(b.gap);
502 }
503 if rest <= 0 {
504 return (iw, 0);
505 }
506 (rest.min(iw), 0)
507 }
508
509 #[cfg_attr(jfgui_dev, track_caller)]
511 pub fn box_begin(&mut self, id: u32, flags: u32, gap: i32) {
512 let _src = self.src_scope();
513 let id = if id != 0 {
514 id
515 } else {
516 let t = self.top();
519 let k = t.id.wrapping_mul(31).wrapping_add(t.nbox);
520 t.nbox = t.nbox.wrapping_add(1);
521 k.wrapping_add(0x2545_F491).wrapping_mul(0x9E37_79B1) | 0x8000_0000
522 };
523 let s = *self.slot(id);
524 let pad = self.dp_space(((flags >> PAD_SHIFT) & 255) as i32); let gap = self.dp_space(gap);
526 let pgrid = self.wp.lay.boxes.last().map_or(0, |b| b.grid);
529 let mw = if wraps(flags) { Some(self.avail_width(s.measw).0).filter(|&w| w > 0).unwrap_or(s.measw) } else { s.measw };
530 self.wp.lay.placing_box = true;
531 let r = self.place(mw, s.meash);
532 self.wp.lay.placing_box = false;
533 let gcell = if pgrid != 0 { self.wp.lay.grid.last } else { (0, [0; 4]) };
534 let auto = self.wp.lay.place_auto;
535 let pmain = self.wp.lay.place_main;
536 if flags & (BOX_BG | BOX_CARD | BOX_LAYER) != 0 {
538 let st = self.style;
539 let mut sp = Sp { flags, ..Sp::default() };
540 sp.place(r);
541 st.box_deco(self, &sp);
542 }
543 if flags & BOX_BLOCK != 0 {
544 self.hit(id, HB_BLOCK);
545 }
546 if flags & BOX_DISABLED != 0 {
547 self.disabled_push();
548 }
549 if flags & BOX_CLIP != 0 {
550 self.clip_push(r[0], r[1], r[2], r[3]);
551 }
552 if self.wp.lay.boxes.len() >= BOX_STACK_MAX {
554 self.warn("boxes nested too deeply");
555 return;
556 }
557 let a = (flags & BOX_V) as usize;
558 let mut b = BoxFrame {
559 id,
560 flags,
561 pos: [r[0].wrapping_add(pad), r[1].wrapping_add(pad)],
562 len: [r[2].wrapping_sub(pad).wrapping_sub(pad).max(0), r[3].wrapping_sub(pad).wrapping_sub(pad).max(0)],
563 gap,
564 pad,
565 auto,
566 pmain,
567 fixedp: s.fixed,
568 wsump: s.wsum,
569 gcell,
570 ..BoxFrame::default()
571 };
572 b.cur = b.pos[a];
575 if wraps(flags) {
576 let prev = self.wp.lay.wrap_rows.get(&id).cloned().unwrap_or_default();
577 b.cur = b.cur.wrapping_add(row_start(flags, b.len[a], prev.first()));
578 self.wp.lay.wraps.push(Wrap { rows: alloc::vec![Row::default()], prev, ..Wrap::default() });
579 } else if flags & (BOX_CENTER | BOX_END) != 0 && b.wsump == 0 {
580 let spare = b.len[a].wrapping_sub(b.fixedp);
581 if spare > 0 {
582 b.cur = b.cur.wrapping_add(if flags & BOX_CENTER != 0 { spare >> 1 } else { spare });
583 }
584 }
585 self.wp.lay.boxes.push(b);
586 if self.m.ins.on {
587 self.ins_box(id, r); }
589 }
590
591 #[cfg_attr(jfgui_dev, track_caller)]
593 pub fn box_end(&mut self) {
594 let _src = self.src_scope();
595 if self.wp.lay.boxes.len() <= 1 {
596 return; }
598 let mut b = *self.top();
599 if wraps(b.flags) {
602 if let Some(w) = self.wp.lay.wraps.pop() {
603 b.cross = w.top.wrapping_add(w.rows.last().map_or(0, |r| r.h));
604 if self.wp.lay.wrap_rows.get(&b.id) != Some(&w.rows) {
605 self.repass = true;
606 self.wp.lay.wrap_rows.insert(b.id, w.rows);
607 }
608 }
609 }
610 let p2 = b.pad.wrapping_add(b.pad);
612 let (mut w, mut h) = (b.fixed.wrapping_add(p2), b.cross.wrapping_add(p2));
613 if b.flags & BOX_V != 0 {
614 core::mem::swap(&mut w, &mut h);
615 }
616 let s = *self.slot(b.id);
617 let size_changed = b.flags & BOX_FIXED == 0 && (w != s.measw || h != s.meash);
618 if size_changed || b.fixed != s.fixed || b.wsum != s.wsum {
619 self.repass = true;
620 if self.ui_debug {
621 let m = alloc::format!(
622 "layout f{}: box {:x} {}x{} -> {}x{} fixed {}->{} wsum {}->{}\n",
623 self.frame as i32, b.id, s.measw, s.meash, w, h, s.fixed, b.fixed, s.wsum, b.wsum
624 );
625 plat::write_stderr(m.as_bytes());
626 }
627 }
628 {
629 let s = self.slot(b.id);
630 s.measw = w;
631 s.meash = h;
632 s.fixed = b.fixed;
633 s.wsum = b.wsum;
634 }
635 let n = self.wp.lay.boxes.len();
638 if n >= 2 && self.wp.lay.boxes[n - 2].grid != 0 && b.gcell.0 != 0 {
639 let k = self.wp.lay.boxes[n - 2].grid as usize;
640 self.grid_child_measured(k, b.gcell.1, w, h);
641 }
642 if n >= 2 {
643 let l = &mut self.wp.lay;
644 let p = &mut l.boxes[n - 2];
645 let (mut along, mut across) = (w, h);
646 if p.flags & BOX_V != 0 {
647 core::mem::swap(&mut along, &mut across);
648 }
649 let mut row = if wraps(p.flags) { l.wraps.last_mut().and_then(|w| w.rows.last_mut()) } else { None };
651 if b.auto & 1 != 0 {
652 p.fixed = p.fixed.wrapping_add(along.wrapping_sub(b.pmain));
653 if let Some(r) = row.as_deref_mut() {
654 r.w = r.w.wrapping_add(along.wrapping_sub(b.pmain));
655 }
656 }
657 if b.auto & 6 != 0 {
661 if across > p.cross {
662 p.cross = across;
663 }
664 if let Some(r) = row.filter(|r| across > r.h) {
665 r.h = across;
666 }
667 }
668 }
669 self.wp.lay.boxes.pop();
670 if self.m.ins.on {
671 self.ins_box_end();
672 }
673 if b.flags & BOX_CLIP != 0 {
674 self.clip_pop();
675 }
676 if b.flags & BOX_DISABLED != 0 {
677 self.disabled_pop();
678 }
679 }
680
681 pub fn spacer(&mut self, dip: i32) {
683 if self.top().flags & BOX_V != 0 {
684 self.wp.lay.next_h = dip;
685 self.wp.lay.next_w = 1;
686 } else {
687 self.wp.lay.next_w = dip;
688 self.wp.lay.next_h = 1;
689 }
690 self.place(0, 0);
691 }
692
693 pub fn dp(&self, dip: i32) -> i32 {
695 dp_at(dip, self.wp.rt.dpi)
696 }
697
698 pub fn px2dip(&self, px: i32) -> i32 {
700 let dpi = self.wp.rt.dpi;
701 px.wrapping_mul(96).wrapping_add(((dpi as u32) >> 1) as i32) / dpi
702 }
703
704 pub fn dpi(&self) -> i32 {
706 self.wp.rt.dpi
707 }
708
709 pub fn set_dpi(&mut self, dpi: i32) {
711 self.wp.rt.dpi = dpi;
712 }
713
714 pub fn dp_fx(&self, dip_fx: i32) -> i32 {
716 dip_fx.wrapping_mul(self.wp.rt.dpi) / 96
717 }
718}
719
720pub fn dp_at(dip: i32, dpi: i32) -> i32 {
722 let v = dip.wrapping_mul(dpi);
723 if v >= 0 { (v + 48) / 96 } else { (v - 48) / 96 }
724}
725
726pub fn px2dip_at(px: i32, dpi: i32) -> i32 {
728 let dpi = if dpi == 0 { 96 } else { dpi };
729 let v = px.wrapping_mul(96);
730 let half = ((dpi as u32) >> 1) as i32;
731 if v >= 0 { v.wrapping_add(half) / dpi } else { v.wrapping_sub(half) / dpi }
732}