1use alloc::collections::BTreeMap;
37use alloc::vec::Vec;
38
39use super::consts::*;
40use super::{Sp, Ui};
41
42pub(crate) const BOX_STACK_MAX: usize = 48;
44
45#[derive(Clone, Copy, Default)]
47pub(crate) struct BoxFrame {
48 pub(crate) id: u32,
49 pub(crate) flags: u32,
50 pub(crate) pos: [i32; 2],
52 pub(crate) len: [i32; 2],
53 pub(crate) cur: i32,
55 pub(crate) gap: i32,
56 pub(crate) fixed: i32,
58 pub(crate) wsum: i32,
59 pub(crate) fixedp: i32,
60 pub(crate) wsump: i32,
61 pub(crate) n: i32,
63 pub(crate) cross: i32,
65 pub(crate) pad: i32,
66 pub(crate) auto: u32,
69 pub(crate) pmain: i32,
71 pub(crate) nbox: u32,
73 pub(crate) grid: u16,
76 pub(crate) gcell: (u16, [u16; 4]),
77 pub(crate) gutter: i32,
80}
81
82#[derive(Clone, Copy, Default, PartialEq)]
85pub(crate) struct Row {
86 pub(crate) h: i32,
87 pub(crate) w: i32,
88}
89
90#[derive(Default)]
93pub(crate) struct Wrap {
94 rows: Vec<Row>,
95 top: i32,
96 n: i32,
97 prev: Vec<Row>,
98}
99
100fn first_fill(b: &BoxFrame) -> i32 {
107 let a = (b.flags & BOX_V) as usize;
108 let gap = if b.n != 0 { b.gap } else { 0 };
109 let rest = b.pos[a].wrapping_add(b.len[a]).wrapping_sub(b.cur).wrapping_sub(gap);
110 rest.min(b.len[a].wrapping_sub(b.fixedp)).max(0)
111}
112
113fn wraps(flags: u32) -> bool {
115 flags & (BOX_WRAP | BOX_V) == BOX_WRAP
116}
117
118fn row_start(flags: u32, len: i32, prev: Option<&Row>) -> i32 {
121 let spare = prev.map_or(0, |r| len.wrapping_sub(r.w));
122 if spare <= 0 {
123 0
124 } else if flags & BOX_CENTER != 0 {
125 spare >> 1
126 } else if flags & BOX_END != 0 {
127 spare
128 } else {
129 0
130 }
131}
132
133#[derive(Default)]
135pub(crate) struct W {
136 pub(crate) boxes: Vec<BoxFrame>,
137 pub(crate) next_w: i32,
138 pub(crate) next_h: i32,
139 pub(crate) next_rx: i32,
140 pub(crate) next_ry: i32,
141 pub(crate) next_rw: i32,
142 pub(crate) next_rh: i32,
143 pub(crate) next_rect_set: u32,
145 pub(crate) next_flags: u32,
146 pub(crate) next_cursor: u32,
148 pub(crate) next_canvas_label: alloc::string::String,
150 pub(crate) next_icon: u32,
151 pub(crate) place_auto: u32,
152 pub(crate) place_main: i32,
153 pub(crate) placing_box: bool,
156 pub(crate) root_id: u32,
158 pub(crate) grid: super::grid::W,
160 pub(crate) wraps: Vec<Wrap>,
162 pub(crate) wrap_rows: BTreeMap<u32, Vec<Row>>,
163}
164
165impl Ui {
166 fn top(&mut self) -> &mut BoxFrame {
169 let b = &mut self.wp.lay.boxes;
170 if b.is_empty() {
171 b.push(BoxFrame::default());
172 }
173 let n = b.len();
174 &mut b[n - 1]
175 }
176
177 pub(crate) fn box_room(&mut self, child: u32) -> (i32, i32, bool) {
181 let prev = self.slot(child).measw;
182 let b = *self.top();
183 if b.flags & BOX_V != 0 {
184 (b.len[0], 0, false)
185 } else {
186 (b.len[0], b.fixedp.wrapping_sub(prev).max(0), b.wsump > 0)
187 }
188 }
189
190 pub(crate) fn layout_root(&mut self, x: i32, y: i32, w: i32, h: i32) {
192 let id = self.wp.lay.root_id;
193 let s = *self.slot(id);
194 let l = &mut self.wp.lay;
195 l.boxes.clear();
196 l.boxes.push(BoxFrame {
197 id,
198 flags: BOX_V,
199 pos: [x, y],
200 len: [w, h],
201 cur: y,
202 fixedp: s.fixed,
203 wsump: s.wsum,
204 ..BoxFrame::default()
205 });
206 l.next_w = 0;
207 l.next_h = 0;
208 l.next_rect_set = 0;
209 l.next_flags = 0;
210 l.next_cursor = 0;
211 l.next_icon = 0;
212 l.grid.runs.clear();
213 l.grid.span = (1, 1);
214 l.grid.cell = None;
215 l.grid.align = (0, 0);
216 l.wraps.clear();
217 }
218
219 pub(crate) fn layout_end_root(&mut self) {
221 while self.wp.lay.boxes.len() > 1 {
222 self.warn("ui_box_begin without ui_box_end");
223 self.box_end();
224 }
225 let b = self.wp.lay.boxes.first().copied().unwrap_or_default();
226 let id = self.wp.lay.root_id;
227 let s = self.slot(id);
228 let changed = b.fixed != s.fixed || b.wsum != s.wsum;
229 s.fixed = b.fixed;
230 s.wsum = b.wsum;
231 if changed {
232 self.repass = true;
233 }
234 self.wp.lay.boxes.clear();
235 }
236
237 pub fn next_size(&mut self, w: i32, h: i32) {
239 self.wp.lay.next_w = w;
240 self.wp.lay.next_h = h;
241 }
242
243 pub fn next_rect(&mut self, x: i32, y: i32, w: i32, h: i32) {
245 let l = &mut self.wp.lay;
246 l.next_rx = x;
247 l.next_ry = y;
248 l.next_rw = w;
249 l.next_rh = h;
250 l.next_rect_set = 1;
251 }
252
253 pub fn next_rect_px(&mut self, x: i32, y: i32, w: i32, h: i32) {
256 let l = &mut self.wp.lay;
257 l.next_rx = x;
258 l.next_ry = y;
259 l.next_rw = w;
260 l.next_rh = h;
261 l.next_rect_set = 2;
262 }
263
264 pub fn box_offset(&mut self, dx: i32, dy: i32) {
266 let b = self.top();
267 b.pos[0] = b.pos[0].wrapping_sub(dx);
268 b.pos[1] = b.pos[1].wrapping_sub(dy);
269 if b.flags & BOX_V != 0 {
270 b.cur = b.cur.wrapping_sub(dy);
271 } else {
272 b.cur = b.cur.wrapping_sub(dx);
273 }
274 }
275
276 pub fn next_flags(&mut self, f: u32) {
278 self.wp.lay.next_flags |= f;
279 }
280
281 pub fn take_flags(&mut self) -> u32 {
283 core::mem::take(&mut self.wp.lay.next_flags)
284 }
285
286 pub fn next_icon(&mut self, cp: u32) {
288 self.wp.lay.next_icon = cp;
289 }
290
291 pub fn take_icon(&mut self) -> u32 {
293 core::mem::take(&mut self.wp.lay.next_icon)
294 }
295
296 pub fn last_rect(&self) -> [i32; 4] {
298 self.wp.ctx.last_rect
299 }
300
301 pub fn box_id(&self) -> u32 {
303 self.wp.lay.boxes.last().map_or(0, |b| b.id)
304 }
305
306 pub fn box_rect(&self) -> [i32; 4] {
308 let b = self.wp.lay.boxes.last().copied().unwrap_or_default();
309 [b.pos[0], b.pos[1], b.len[0], b.len[1]]
310 }
311
312 pub fn box_flags(&self) -> u32 {
314 self.wp.lay.boxes.last().map_or(0, |b| b.flags)
315 }
316
317 pub fn place(&mut self, iw: i32, ih: i32) -> [i32; 4] {
319 let dpi = self.wp.rt.dpi;
320 let dp = |v: i32| dp_at(v, dpi);
321 let mut repass = false;
322 self.top();
323 let gk = self.wp.lay.boxes.last().map_or(0, |b| b.grid);
325 if gk != 0 && self.wp.lay.next_rect_set == 0 {
326 let r = self.grid_place(gk as usize, iw, ih);
327 self.wp.ctx.last_rect = r;
328 return r;
329 }
330 let l = &mut self.wp.lay;
331 let r = if l.next_rect_set == 0 {
332 let n = l.boxes.len();
333 let b = &mut l.boxes[n - 1];
334 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 };
338 let (mut rmain, mut rcross) = (l.next_w, l.next_h);
340 let (nat_main, mut nat_cross) = if a == 1 { (ih, iw) } else { (iw, ih) };
341 if a == 1 {
342 core::mem::swap(&mut rmain, &mut rcross);
343 }
344 let aligned = b.flags & (BOX_XCENTER | BOX_XSTART | BOX_XEND) != 0;
346 let mut auto = 0;
347 if rmain == 0 {
348 auto |= 1;
349 }
350 if rcross == 0 && aligned {
351 auto |= 2;
352 } else if rcross <= 0 {
353 auto |= 4;
355 }
356 l.place_auto = auto;
357 let mut weight = 0;
360 let main = if rmain == 0 {
361 nat_main
362 } else if rmain < 0 {
363 weight = rmain.wrapping_neg();
364 let spare = (b.len[a].wrapping_sub(b.fixedp)).max(0);
365 if b.wsump == 0 {
366 repass = true;
370 if wr.is_some() { nat_main } else { first_fill(b) }
371 } else if wr.is_some() && spare == 0 {
372 nat_main
373 } else {
374 spare.wrapping_mul(weight) / b.wsump
375 }
376 } else {
377 dp(rmain)
378 };
379 if let Some(w) = wr.as_deref_mut() {
382 let used = w.rows.last().map_or(0, |r| r.w);
383 if w.n != 0 && used.wrapping_add(b.gap).wrapping_add(main) > b.len[a] {
384 let h = w.rows.last().map_or(0, |r| r.h);
385 w.top = w.top.wrapping_add(h).wrapping_add(b.gap);
386 w.rows.push(Row::default());
387 w.n = 0;
388 b.cur = b.pos[a].wrapping_add(row_start(b.flags, b.len[a], w.prev.get(w.rows.len() - 1)));
389 }
390 }
391 let (start, room) = match wr.as_deref() {
394 Some(w) => (b.pos[c].wrapping_add(w.top), w.prev.get(w.rows.len() - 1).map_or(0, |p| p.h)),
395 None => (b.pos[c], b.len[c]),
396 };
397 let cross = if rcross > 0 {
399 nat_cross = dp(rcross); nat_cross
401 } else if (rcross == 0 && aligned) || (wr.is_some() && room == 0) {
402 nat_cross
403 } else {
404 room
405 };
406 let gap = match wr.as_deref() {
409 Some(w) if w.n == 0 => 0,
410 None if b.n == 0 => 0,
411 _ => b.gap,
412 };
413 if b.n != 0 {
414 b.fixed = b.fixed.wrapping_add(b.gap);
415 }
416 b.cur = b.cur.wrapping_add(gap);
417 b.n += 1;
418 l.place_main = main;
419 if weight == 0 {
420 b.fixed = b.fixed.wrapping_add(main);
421 } else {
422 b.wsum = b.wsum.wrapping_add(weight);
423 }
424 let counts = !(l.placing_box && auto & 6 != 0);
425 if nat_cross > b.cross && counts {
426 b.cross = nat_cross;
427 }
428 if let Some(w) = wr.as_deref_mut() {
429 w.n += 1;
430 if let Some(row) = w.rows.last_mut() {
431 row.w = row.w.wrapping_add(gap).wrapping_add(main);
432 if nat_cross > row.h && counts {
433 row.h = nat_cross;
434 }
435 }
436 }
437 let mut r = [0i32; 4];
439 r[a] = b.cur;
440 r[2 + a] = main;
441 b.cur = b.cur.wrapping_add(main);
442 let mut spare = room.wrapping_sub(cross);
443 if wr.is_some() {
444 spare = spare.max(0); }
446 let mut cp = start;
447 if b.flags & BOX_XCENTER != 0 {
448 cp = cp.wrapping_add(spare >> 1);
449 } else if b.flags & BOX_XEND != 0 {
450 cp = cp.wrapping_add(spare);
451 }
452 r[c] = cp;
453 r[2 + c] = cross;
454 r
455 } else if l.next_rect_set == 2 {
456 l.next_rect_set = 0;
458 l.place_auto = 0;
459 [
460 l.next_rx,
461 l.next_ry,
462 if l.next_rw != 0 { l.next_rw } else { iw },
463 if l.next_rh != 0 { l.next_rh } else { ih },
464 ]
465 } else {
466 l.next_rect_set = 0;
468 l.place_auto = 0;
469 let n = l.boxes.len();
470 let b = l.boxes[n - 1];
471 [
472 dp(l.next_rx).wrapping_add(b.pos[0]),
473 dp(l.next_ry).wrapping_add(b.pos[1]),
474 if l.next_rw != 0 { dp(l.next_rw) } else { iw },
475 if l.next_rh != 0 { dp(l.next_rh) } else { ih },
476 ]
477 };
478 l.next_w = 0;
479 l.next_h = 0;
480 self.wp.ctx.last_rect = r;
481 if repass {
482 self.repass = true;
483 }
484 r
485 }
486
487 pub fn avail_width(&self, iw: i32) -> (i32, i32) {
491 let l = &self.wp.lay;
492 if l.next_rect_set != 0 {
493 if l.next_rw == 0 {
494 return (iw, 0);
495 }
496 return (if l.next_rect_set == 2 { l.next_rw } else { self.dp(l.next_rw) }, 1);
497 }
498 let b = l.boxes.last().copied().unwrap_or_default();
499 if b.grid != 0 {
501 return self.grid_avail_width(b.grid as usize, iw);
502 }
503 let w = l.next_w;
504 if b.flags & BOX_V != 0 {
505 if w > 0 {
507 return (self.dp(w), 1);
508 }
509 if w < 0 || b.flags & (BOX_XCENTER | BOX_XSTART | BOX_XEND) == 0 {
510 return (b.len[0], 1);
511 }
512 if b.len[0] <= 0 {
514 return (iw, 0);
515 }
516 return (b.len[0].min(iw), 0);
517 }
518 if w > 0 {
519 return (self.dp(w), 1);
520 }
521 let wrap = wraps(b.flags);
522 let spare = b.len[0].wrapping_sub(b.fixedp).max(0);
523 if w < 0 && !(wrap && (b.wsump == 0 || spare == 0)) {
524 if b.wsump == 0 {
527 return (first_fill(&b), 1);
528 }
529 return (spare.wrapping_mul(w.wrapping_neg()) / b.wsump, 1);
530 }
531 if wrap {
532 if b.len[0] <= 0 {
535 return (iw, 0);
536 }
537 return (b.len[0].min(iw), 0);
538 }
539 let mut rest = b.pos[0].wrapping_add(b.len[0]).wrapping_sub(b.cur);
541 if b.n != 0 {
542 rest = rest.wrapping_sub(b.gap);
543 }
544 if rest <= 0 {
545 return (iw, 0);
546 }
547 (rest.min(iw), 0)
548 }
549
550 #[cfg_attr(jfgui_dev, track_caller)]
552 pub fn box_begin(&mut self, id: u32, flags: u32, gap: i32) {
553 let _src = self.src_scope();
554 let id = if id != 0 {
555 id
556 } else {
557 let t = self.top();
560 let k = t.id.wrapping_mul(31).wrapping_add(t.nbox);
561 t.nbox = t.nbox.wrapping_add(1);
562 k.wrapping_add(0x2545_F491).wrapping_mul(0x9E37_79B1) | 0x8000_0000
563 };
564 let s = *self.slot(id);
565 let pad = self.dp_space(((flags >> PAD_SHIFT) & 255) as i32); let gap = self.dp_space(gap);
567 let pgrid = self.wp.lay.boxes.last().map_or(0, |b| b.grid);
570 let mw = if wraps(flags) { Some(self.avail_width(s.measw).0).filter(|&w| w > 0).unwrap_or(s.measw) } else { s.measw };
571 self.wp.lay.placing_box = true;
572 let r = self.place(mw, s.meash);
573 self.wp.lay.placing_box = false;
574 let gcell = if pgrid != 0 { self.wp.lay.grid.last } else { (0, [0; 4]) };
575 let auto = self.wp.lay.place_auto;
576 let pmain = self.wp.lay.place_main;
577 if flags & (BOX_BG | BOX_CARD | BOX_LAYER) != 0 {
579 let st = self.style;
580 let mut sp = Sp { flags, ..Sp::default() };
581 sp.place(r);
582 st.box_deco(self, &sp);
583 }
584 if flags & BOX_BLOCK != 0 {
585 self.hit(id, HB_BLOCK);
586 }
587 if flags & BOX_DISABLED != 0 {
588 self.disabled_push();
589 }
590 if flags & BOX_CLIP != 0 {
591 self.clip_push(r[0], r[1], r[2], r[3]);
592 }
593 if self.wp.lay.boxes.len() >= BOX_STACK_MAX {
595 self.warn("boxes nested too deeply");
596 return;
597 }
598 let a = (flags & BOX_V) as usize;
599 let mut b = BoxFrame {
600 id,
601 flags,
602 pos: [r[0].wrapping_add(pad), r[1].wrapping_add(pad)],
603 len: [r[2].wrapping_sub(pad).wrapping_sub(pad).max(0), r[3].wrapping_sub(pad).wrapping_sub(pad).max(0)],
604 gap,
605 pad,
606 auto,
607 pmain,
608 fixedp: s.fixed,
609 wsump: s.wsum,
610 gcell,
611 ..BoxFrame::default()
612 };
613 b.cur = b.pos[a];
616 if wraps(flags) {
617 let prev = self.wp.lay.wrap_rows.get(&id).cloned().unwrap_or_default();
618 b.cur = b.cur.wrapping_add(row_start(flags, b.len[a], prev.first()));
619 self.wp.lay.wraps.push(Wrap { rows: alloc::vec![Row::default()], prev, ..Wrap::default() });
620 } else if flags & (BOX_CENTER | BOX_END) != 0 && b.wsump == 0 {
621 let spare = b.len[a].wrapping_sub(b.fixedp);
622 if spare > 0 {
623 b.cur = b.cur.wrapping_add(if flags & BOX_CENTER != 0 { spare >> 1 } else { spare });
624 }
625 }
626 self.wp.lay.boxes.push(b);
627 if self.m.ins.on {
628 self.ins_box(id, r); }
630 }
631
632 #[cfg_attr(jfgui_dev, track_caller)]
634 pub fn box_end(&mut self) {
635 let _src = self.src_scope();
636 if self.wp.lay.boxes.len() <= 1 {
637 return; }
639 let mut b = *self.top();
640 if wraps(b.flags) {
643 if let Some(w) = self.wp.lay.wraps.pop() {
644 b.cross = w.top.wrapping_add(w.rows.last().map_or(0, |r| r.h));
645 if self.wp.lay.wrap_rows.get(&b.id) != Some(&w.rows) {
646 self.repass = true;
647 self.wp.lay.wrap_rows.insert(b.id, w.rows);
648 }
649 }
650 }
651 let p2 = b.pad.wrapping_add(b.pad);
653 let (mut w, mut h) = (b.fixed.wrapping_add(p2), b.cross.wrapping_add(p2));
654 if b.flags & BOX_V != 0 {
655 core::mem::swap(&mut w, &mut h);
656 }
657 w = w.wrapping_add(b.gutter);
658 let s = *self.slot(b.id);
659 let size_changed = b.flags & BOX_FIXED == 0 && (w != s.measw || h != s.meash);
660 if size_changed || b.fixed != s.fixed || b.wsum != s.wsum {
661 self.repass = true;
662 if self.ui_debug {
663 let m = alloc::format!(
664 "layout f{}: box {:x} {}x{} -> {}x{} fixed {}->{} wsum {}->{}\n",
665 self.frame as i32, b.id, s.measw, s.meash, w, h, s.fixed, b.fixed, s.wsum, b.wsum
666 );
667 plat::write_stderr(m.as_bytes());
668 }
669 }
670 {
671 let s = self.slot(b.id);
672 s.measw = w;
673 s.meash = h;
674 s.fixed = b.fixed;
675 s.wsum = b.wsum;
676 }
677 let n = self.wp.lay.boxes.len();
680 if n >= 2 && self.wp.lay.boxes[n - 2].grid != 0 && b.gcell.0 != 0 {
681 let k = self.wp.lay.boxes[n - 2].grid as usize;
682 self.grid_child_measured(k, b.gcell.1, w, h);
683 }
684 if n >= 2 {
685 let l = &mut self.wp.lay;
686 let p = &mut l.boxes[n - 2];
687 let (mut along, mut across) = (w, h);
688 if p.flags & BOX_V != 0 {
689 core::mem::swap(&mut along, &mut across);
690 }
691 let mut row = if wraps(p.flags) { l.wraps.last_mut().and_then(|w| w.rows.last_mut()) } else { None };
693 if b.auto & 1 != 0 {
694 p.fixed = p.fixed.wrapping_add(along.wrapping_sub(b.pmain));
695 if let Some(r) = row.as_deref_mut() {
696 r.w = r.w.wrapping_add(along.wrapping_sub(b.pmain));
697 }
698 }
699 if b.auto & 6 != 0 {
703 if across > p.cross {
704 p.cross = across;
705 }
706 if let Some(r) = row.filter(|r| across > r.h) {
707 r.h = across;
708 }
709 }
710 }
711 self.wp.lay.boxes.pop();
712 if self.m.ins.on {
713 self.ins_box_end();
714 }
715 if b.flags & BOX_CLIP != 0 {
716 self.clip_pop();
717 }
718 if b.flags & BOX_DISABLED != 0 {
719 self.disabled_pop();
720 }
721 }
722
723 pub fn spacer(&mut self, dip: i32) {
725 if self.top().flags & BOX_V != 0 {
726 self.wp.lay.next_h = dip;
727 self.wp.lay.next_w = 1;
728 } else {
729 self.wp.lay.next_w = dip;
730 self.wp.lay.next_h = 1;
731 }
732 self.place(0, 0);
733 }
734
735 pub fn dp(&self, dip: i32) -> i32 {
737 dp_at(dip, self.wp.rt.dpi)
738 }
739
740 pub fn px2dip(&self, px: i32) -> i32 {
742 let dpi = self.wp.rt.dpi;
743 px.wrapping_mul(96).wrapping_add(((dpi as u32) >> 1) as i32) / dpi
744 }
745
746 pub fn dpi(&self) -> i32 {
748 self.wp.rt.dpi
749 }
750
751 pub fn set_dpi(&mut self, dpi: i32) {
753 self.wp.rt.dpi = dpi;
754 }
755
756 pub fn dp_fx(&self, dip_fx: i32) -> i32 {
758 dip_fx.wrapping_mul(self.wp.rt.dpi) / 96
759 }
760}
761
762pub fn dp_at(dip: i32, dpi: i32) -> i32 {
764 let v = dip.wrapping_mul(dpi);
765 if v >= 0 { (v + 48) / 96 } else { (v - 48) / 96 }
766}
767
768pub fn px2dip_at(px: i32, dpi: i32) -> i32 {
770 let dpi = if dpi == 0 { 96 } else { dpi };
771 let v = px.wrapping_mul(96);
772 let half = ((dpi as u32) >> 1) as i32;
773 if v >= 0 { v.wrapping_add(half) / dpi } else { v.wrapping_sub(half) / dpi }
774}