1use crate::scene::{self, Align, ImageFiles, Layout, Node, Scene, Size as DSize};
5use crate::tree::{Order, OrderKey};
6
7use super::*;
8
9mod run;
10pub(super) use run::*;
11impl Renderer {
12 pub fn layout(&mut self, scene: &Scene) -> Vec<Frame> {
15 self.register(&scene.fonts);
16 let mut l = Laid::build(self, scene);
17 l.settle_top(self);
18 l.frames(self, &scene.root).into_values().collect()
19 }
20
21 pub(super) fn compute(
22 &mut self,
23 runs: &mut Runs,
24 tree: &mut TaffyTree<Option<Leaf>>,
25 root: NodeId,
26 available: Size<AvailableSpace>,
27 ) {
28 tree.compute_layout_with_measure(root, available, |input, _id, ctx, style| {
29 let known = input.known_dimensions;
30 let fixed = |d: Dimension| (!d.is_auto()).then(|| d.value());
31 let known = Size {
32 width: known.width.or(fixed(style.size.width)),
33 height: known.height.or(fixed(style.size.height)),
34 };
35 let size = match ctx {
36 Some(Some(Leaf::Intrinsic(w, h))) => {
37 let (w, h) = (*w, *h);
39 match (known.width, known.height) {
40 (Some(kw), None) => Size {
41 width: kw,
42 height: kw * h / w,
43 },
44 (None, Some(kh)) => Size {
45 width: kh * w / h,
46 height: kh,
47 },
48 (kw, kh) => Size {
49 width: kw.unwrap_or(w),
50 height: kh.unwrap_or(h),
51 },
52 }
53 }
54 Some(Some(Leaf::Run(id))) => {
55 let [t, r, b, l] = runs.get(*id).padding;
59 let own = Size {
60 width: fixed(style.size.width).map(|v| v.max(l + r)),
61 height: fixed(style.size.height).map(|v| v.max(t + b)),
62 };
63 let known = Size {
64 width: input.known_dimensions.width.or(own.width),
65 height: input.known_dimensions.height.or(own.height),
66 };
67 return taffy::LayoutOutput::from_outer_size(self.measure_run(
68 runs,
69 *id,
70 known,
71 input.available_space,
72 ));
73 }
74 Some(Some(Leaf::Text(text, wraps))) => {
75 let width = known.width.or(match input.available_space.width {
76 AvailableSpace::Definite(w) if *wraps => Some(w),
77 _ => None,
78 });
79 let layout = self.text_layout(text, width);
80 Size {
81 width: known.width.unwrap_or(layout.width().ceil()),
82 height: known.height.unwrap_or(layout.height()),
83 }
84 }
85 _ => Size {
86 width: known.width.unwrap_or(0.0),
87 height: known.height.unwrap_or(0.0),
88 },
89 };
90 taffy::LayoutOutput::from_outer_size(size)
91 })
92 .expect("layout");
93 }
94}
95
96impl Renderer {
97 pub(super) fn settle_in(
100 &mut self,
101 runs: &mut Runs,
102 tree: &mut TaffyTree<Option<Leaf>>,
103 root: NodeId,
104 available: Size<AvailableSpace>,
105 hugs: &[(NodeId, bool, bool)],
106 ) {
107 for _ in 0..256 {
111 self.compute(runs, tree, root, available);
112 let mut changed = false;
113 for &(id, w, h) in hugs {
114 let (mut ew, mut eh) = (0.0f32, 0.0f32);
115 for child in tree.children(id).expect("children") {
116 let l = tree.unrounded_layout(child);
117 ew = ew.max(l.location.x + l.size.width);
118 eh = eh.max(l.location.y + l.size.height);
119 }
120 let mut style = tree.style(id).expect("style").clone();
121 let mut ch = false;
122 let mut set = |d: &mut Dimension, v: f32| {
123 if *d != Dimension::length(v) {
124 *d = Dimension::length(v);
125 ch = true;
126 }
127 };
128 if w {
129 set(&mut style.size.width, ew);
130 }
131 if h {
132 set(&mut style.size.height, eh);
133 }
134 if ch {
135 tree.set_style(id, style).expect("style");
136 changed = true;
137 }
138 }
139 if !changed {
140 break;
141 }
142 }
143 }
144}
145
146pub(super) fn find_node<'a>(root: &'a Node, order: &[OrderKey]) -> &'a Node {
147 let mut at = root;
148 for k in order {
149 at = at.children.get(k).expect("a node there");
150 }
151 at
152}
153
154pub(super) fn remove_subtree(tree: &mut TaffyTree<Option<Leaf>>, id: NodeId) {
156 for child in tree.children(id).unwrap_or_default() {
157 remove_subtree(tree, child);
158 }
159 let _ = tree.remove(id);
160}
161
162pub(super) enum Leaf {
165 Text(Box<scene::TextStyle>, bool),
167 Intrinsic(f32, f32),
168 Run(u32),
170 Proxy(OrderKey),
173}
174
175pub(super) struct Laid {
178 pub(super) top: TaffyTree<Option<Leaf>>,
179 pub(super) root: NodeId,
180 pub(super) runs: Runs,
181 pub(super) nodes: BTreeMap<Order, (Owner, NodeId)>,
183 pub(super) run_of: BTreeMap<Order, u32>,
185 pub(super) sens: BTreeMap<Order, Sens>,
189}
190
191#[derive(Clone, Copy, Debug, Default)]
194pub(super) struct Sens {
195 pub(super) me: bool,
196 pub(super) w: bool,
197 pub(super) h: bool,
198 pub(super) kids_w: usize,
199 pub(super) kids_h: usize,
200}
201
202fn room(node: &Node, kids_w: usize, kids_h: usize) -> (bool, bool) {
208 let along = |s: DSize, kids: usize| match s {
209 DSize::Px(_) => false,
210 DSize::Fill => true,
211 DSize::Hug => kids > 0,
212 };
213 (along(node.width, kids_w), along(node.height, kids_h))
214}
215
216impl Laid {
217 pub(super) fn sens_build(&mut self, node: &Node, order: Order) -> (bool, bool) {
220 let (mut kids_w, mut kids_h) = (0, 0);
221 for (k, c) in node.children.entries() {
222 let mut o = order.clone();
223 o.push(k.clone());
224 let (w, h) = self.sens_build(c, o);
225 kids_w += w as usize;
226 kids_h += h as usize;
227 }
228 let (w, h) = room(node, kids_w, kids_h);
229 self.sens.insert(
230 order,
231 Sens {
232 me: w || h,
233 w,
234 h,
235 kids_w,
236 kids_h,
237 },
238 );
239 (w, h)
240 }
241
242 pub(super) fn sensitive(&self, order: &Order) -> bool {
244 self.sens.get(order).is_some_and(|s| s.me)
245 }
246
247 pub(super) fn sens_settle(&mut self, root: &Node, order: &Order, kids: (isize, isize)) {
252 let mut at = order.clone();
253 let mut delta = kids;
254 loop {
255 let node = find_node(root, &at);
256 let s = self.sens.entry(at.clone()).or_default();
257 s.kids_w = (s.kids_w as isize + delta.0).max(0) as usize;
258 s.kids_h = (s.kids_h as isize + delta.1).max(0) as usize;
259 let (was_w, was_h, was) = (s.w, s.h, s.me);
260 let (w, h) = room(node, s.kids_w, s.kids_h);
261 (s.w, s.h, s.me) = (w, h, w || h);
262 if at.is_empty() {
263 return;
264 }
265 let parent = at[..at.len() - 1].to_vec();
266 if was != s.me
267 && let Some(&rid) = self.run_of.get(&parent)
268 {
269 let now = s.me;
270 let k = at.last().expect("a key").clone();
271 let run = self.runs.get_mut(rid);
272 let stretch = run.children.get(&k).is_some_and(|c| c.stretch);
273 run.classify(&k, stretch, now);
274 }
275 if (was_w, was_h) == (w, h) {
276 return;
277 }
278 delta = (w as isize - was_w as isize, h as isize - was_h as isize);
279 at = parent;
280 }
281 }
282
283 pub(super) fn sens_drop(&mut self, root: &Node, order: &Order) {
286 let (w, h) = self.sens.get(order).map_or((false, false), |s| (s.w, s.h));
287 let under: Vec<Order> = self
288 .sens
289 .range(order.clone()..)
290 .take_while(|(o, _)| o.starts_with(order))
291 .map(|(o, _)| o.clone())
292 .collect();
293 for o in under {
294 self.sens.remove(&o);
295 }
296 if (w || h) && !order.is_empty() {
297 let parent = order[..order.len() - 1].to_vec();
298 if self.sens.contains_key(&parent) {
299 self.sens_settle(root, &parent, (-(w as isize), -(h as isize)));
300 }
301 }
302 }
303}
304
305use std::collections::BTreeMap;
306
307impl Laid {
308 pub(super) fn build(_r: &mut Renderer, scene: &Scene) -> Laid {
310 let mut top = TaffyTree::new();
311 top.disable_rounding();
314 let mut l = Laid {
315 top: TaffyTree::new(),
316 root: NodeId::from(0u64),
317 runs: Runs::default(),
318 nodes: BTreeMap::new(),
319 run_of: BTreeMap::new(),
320 sens: BTreeMap::new(),
321 };
322 l.sens_build(&scene.root, Vec::new());
323 let root = l.build_node(
324 &mut top,
325 Owner::Top,
326 None,
327 &scene.root,
328 None,
329 &scene.images,
330 Vec::new(),
331 );
332 l.top = top;
333 l.root = root;
334 l
335 }
336
337 pub(super) fn tree(&self, owner: Owner) -> &TaffyTree<Option<Leaf>> {
338 match owner {
339 Owner::Top => &self.top,
340 Owner::Run(id) => &self.runs.get(id).tree,
341 }
342 }
343
344 pub(super) fn tree_mut(&mut self, owner: Owner) -> &mut TaffyTree<Option<Leaf>> {
345 match owner {
346 Owner::Top => &mut self.top,
347 Owner::Run(id) => &mut self.runs.get_mut(id).tree,
348 }
349 }
350
351 #[allow(clippy::too_many_arguments)]
355 pub(super) fn build_node(
356 &mut self,
357 tree: &mut TaffyTree<Option<Leaf>>,
358 owner: Owner,
359 inside: Option<OrderKey>,
360 node: &Node,
361 parent: Option<&Node>,
362 images: &ImageFiles,
363 order: Order,
364 ) -> NodeId {
365 let style = style_of(node, parent);
366 if let Some(leaf) = leaf_of(node, images) {
367 let id = tree.new_leaf_with_context(style, Some(leaf)).expect("leaf");
368 self.nodes.insert(order, (owner, id));
369 return id;
370 }
371 if is_run(node) {
372 let rid = self.runs.reserve();
373 let leaf = tree
377 .new_leaf_with_context(style, Some(Leaf::Run(rid)))
378 .expect("leaf");
379 let mut run = RunLayout::new(node, order.clone(), owner, leaf, inside);
380 run.hug = hugs_by_extent(node, parent).unwrap_or((false, false));
381 for (k, c) in node.children.entries() {
382 self.add_child(&mut run, rid, k, c, node, images, &order);
383 }
384 self.runs.put(rid, run);
385 self.run_of.insert(order.clone(), rid);
386 self.nodes.insert(order, (owner, leaf));
387 return leaf;
388 }
389 let id = tree.new_leaf(style).expect("leaf");
391 self.nodes.insert(order, (owner, id));
392 id
393 }
394
395 #[allow(clippy::too_many_arguments)]
398 pub(super) fn add_child(
399 &mut self,
400 run: &mut RunLayout,
401 rid: u32,
402 k: &OrderKey,
403 child: &Node,
404 node: &Node,
405 images: &ImageFiles,
406 order: &Order,
407 ) {
408 let mut o = order.clone();
409 o.push(k.clone());
410 let mut tree = std::mem::replace(&mut run.tree, TaffyTree::new());
411 let id = self.build_node(
412 &mut tree,
413 Owner::Run(rid),
414 Some(k.clone()),
415 child,
416 Some(node),
417 images,
418 o,
419 );
420 run.tree = tree;
421 let (along, across) = if run.row {
422 (child.width, child.height)
423 } else {
424 (child.height, child.width)
425 };
426 let mut o = order.clone();
427 o.push(k.clone());
428 let sens = self.sensitive(&o);
429 run.add(k.clone(), id, across == DSize::Fill, sens);
430 if along == DSize::Fill {
431 run.add_fill(k.clone(), style_of(child, Some(node)));
432 }
433 }
434
435 pub(super) fn settle_top(&mut self, r: &mut Renderer) {
437 let root = self.root;
438 r.settle_in(&mut self.runs, &mut self.top, root, Size::MAX_CONTENT, &[]);
439 }
440
441 pub(super) fn run_places(
446 &self,
447 rid: u32,
448 size: Size<f32>,
449 ) -> Option<(&RunLayout, &Asked, f32, f64)> {
450 let run = self.runs.get(rid);
451 let (pa0, pa1) = run.padding_along();
452 let (pc0, pc1) = run.padding_across();
453 let (along, across) = if run.row {
454 (size.width, size.height)
455 } else {
456 (size.height, size.width)
457 };
458 let line = (across - pc0 - pc1).max(0.0);
459 let asked = run.placed(line)?;
460 let free = along - pa0 - pa1 - run.length(asked) as f32;
461 let n = run.children.len();
462 let spread = if run.justify == Align::SpaceBetween && free > 1e-3 && n > 1 {
463 free as f64 / (n - 1) as f64
464 } else {
465 0.0
466 };
467 Some((run, asked, pa0 + run.lead(free), run.gap as f64 + spread))
468 }
469
470 pub(super) fn frames(&mut self, r: &mut Renderer, root: &Node) -> BTreeMap<Order, Frame> {
472 let mut out = BTreeMap::new();
473 let top = self.root;
474 self.frames_of(
475 r,
476 root,
477 Vec::new(),
478 Owner::Top,
479 top,
480 Place::default(),
481 true,
482 &mut out,
483 );
484 out
485 }
486
487 #[allow(clippy::too_many_arguments)]
488 fn frames_of(
489 &mut self,
490 r: &mut Renderer,
491 node: &Node,
492 order: Order,
493 owner: Owner,
494 id: NodeId,
495 parent: Place,
496 root: bool,
497 out: &mut BTreeMap<Order, Frame>,
498 ) {
499 let tree = self.tree(owner);
500 let (frame, place) = placed(tree, id, parent, root);
501 out.insert(order.clone(), frame);
502 if let Some(&rid) = self.run_of.get(&order)
503 && self.runs.get(rid).free
504 {
505 for (k, c) in node.children.entries() {
507 r.relay_free(&mut self.runs, rid, k);
508 let node_id = self.runs.get(rid).children[k].node;
509 let mut o = order.clone();
510 o.push(k.clone());
511 self.frames_of(r, c, o, Owner::Run(rid), node_id, place, false, out);
512 }
513 return;
514 }
515 if let Some(&rid) = self.run_of.get(&order) {
516 let size = tree.unrounded_layout(id).size;
517 let mut before = 0.0f64;
518 for (k, c) in node.children.entries() {
519 let Some((run, asked, lead, step)) = self.run_places(rid, size) else {
520 return;
521 };
522 let line = asked.line;
523 let Some(s) = run.size_of(asked, k) else {
524 continue;
525 };
526 let (pc0, _) = run.padding_across();
527 let stretch = run.children[k].stretch;
528 let across = pc0 + run.across(line, &s, stretch);
529 let item = child_place(place, run.row, lead as f64 + before, across as f64);
530 let node_id = run.children[k].node;
531 r.relay(&mut self.runs, rid, k, line);
532 let mut o = order.clone();
533 o.push(k.clone());
534 self.frames_of(r, c, o, Owner::Run(rid), node_id, item, false, out);
535 before += s.along as f64 + step;
536 }
537 return;
538 }
539 let kids = tree.children(id).unwrap_or_default();
540 for ((k, c), &child) in node.children.entries().zip(&kids) {
541 let mut o = order.clone();
542 o.push(k.clone());
543 self.frames_of(r, c, o, owner, child, place, false, out);
544 }
545 }
546}
547
548pub(super) fn child_place(run: Place, row: bool, along: f64, across: f64) -> Place {
553 let (along, across) = (along as f32, across as f32);
556 let (dx, dy) = if row {
557 (along, across)
558 } else {
559 (across, along)
560 };
561 let raw = (run.raw.0 + dx, run.raw.1 + dy);
562 let at = (
563 run.at.0 + snap(raw.0) - snap(run.raw.0),
564 run.at.1 + snap(raw.1) - snap(run.raw.1),
565 );
566 Place { at, raw }
567}
568
569#[cfg(test)]
573pub(super) fn build_plain(
574 tree: &mut TaffyTree<Option<Leaf>>,
575 node: &Node,
576 parent: Option<&Node>,
577 hugs: &mut Vec<(NodeId, bool, bool)>,
578 images: &ImageFiles,
579) -> NodeId {
580 let style = style_of(node, parent);
581 if let Some(leaf) = leaf_of(node, images) {
582 return tree.new_leaf_with_context(style, Some(leaf)).expect("leaf");
583 }
584 let children: Vec<NodeId> = node
585 .children
586 .iter()
587 .map(|c| build_plain(tree, c, Some(node), hugs, images))
588 .collect();
589 let id = tree.new_with_children(style, &children).expect("node");
590 if let Some((w, h)) = hugs_by_extent(node, parent) {
591 hugs.push((id, w, h));
592 }
593 id
594}
595
596pub(super) fn hugs_by_extent(node: &Node, parent: Option<&Node>) -> Option<(bool, bool)> {
599 if leafy(node) {
600 return None;
601 }
602 let hugs_by = |s: DSize| s == DSize::Hug || (parent.is_none() && s == DSize::Fill);
604 let (w, h) = (hugs_by(node.width), hugs_by(node.height));
605 (node.layout == Layout::None && !node.children.is_empty() && (w || h)).then_some((w, h))
606}
607
608pub(super) fn leafy(node: &Node) -> bool {
610 node.text.is_some() || node.vector.is_some() || node.image.is_some()
611}
612
613pub(super) fn leaf_of(node: &Node, images: &ImageFiles) -> Option<Leaf> {
615 if let Some(text) = &node.text {
616 let wraps = !matches!(node.width, DSize::Hug);
617 return Some(Leaf::Text(Box::new(text.clone()), wraps));
618 }
619 let intrinsic = match (&node.vector, &node.image) {
620 (Some(v), _) => Some((v.view_box[2], v.view_box[3])),
621 (_, Some(i)) => images.get(&i.id).map(|f| (f.width as f32, f.height as f32)),
622 _ => None,
623 };
624 intrinsic.map(|(w, h)| Leaf::Intrinsic(w, h))
625}
626
627pub(super) fn style_of(node: &Node, parent: Option<&Node>) -> Style {
629 let mut style = Style::default();
630 let dir = parent.map(|p| p.layout);
631 let dim = |s: DSize| match s {
632 DSize::Px(v) => Dimension::length(v),
633 _ => Dimension::auto(),
634 };
635 style.size = Size {
636 width: dim(node.width),
637 height: dim(node.height),
638 };
639 let (main, cross) = match dir {
640 Some(Layout::Row) => (node.width, node.height),
641 Some(Layout::Column) => (node.height, node.width),
642 _ => (DSize::Hug, DSize::Hug),
643 };
644 if main == DSize::Fill {
645 style.flex_grow = 1.0;
646 style.flex_basis = Dimension::length(0.0);
647 }
648 if cross == DSize::Fill {
649 style.align_self = Some(AlignSelf::STRETCH);
650 }
651 style.position = Position::Relative;
655 if dir == Some(Layout::None) {
656 style.position = Position::Absolute;
657 style.inset = taffy::Rect {
658 left: LengthPercentageAuto::length(node.x),
659 top: LengthPercentageAuto::length(node.y),
660 right: LengthPercentageAuto::auto(),
661 bottom: LengthPercentageAuto::auto(),
662 };
663 }
664 style.flex_shrink = 0.0;
665 if node.layout == Layout::None && !leafy(node) {
668 let [t, r, b, l] = node.padding;
669 style.min_size = Size {
670 width: LengthPercentageAuto::length(l + r),
671 height: LengthPercentageAuto::length(t + b),
672 };
673 }
674 match node.layout {
675 Layout::Row => style.flex_direction = FlexDirection::Row,
676 Layout::Column => style.flex_direction = FlexDirection::Column,
677 Layout::None => {}
678 }
679 style.gap = Size {
680 width: LengthPercentage::length(node.gap),
681 height: LengthPercentage::length(node.gap),
682 };
683 let [t, r, b, l] = node.padding;
684 style.padding = taffy::Rect {
685 top: LengthPercentage::length(t),
686 right: LengthPercentage::length(r),
687 bottom: LengthPercentage::length(b),
688 left: LengthPercentage::length(l),
689 };
690 style.align_items = match node.align {
691 Align::Center => AlignItems::CENTER,
692 Align::End => AlignItems::FLEX_END,
693 _ => AlignItems::FLEX_START,
694 };
695 style.justify_content = match node.justify {
696 Align::Center => JustifyContent::CENTER,
697 Align::End => JustifyContent::FLEX_END,
698 Align::SpaceBetween => JustifyContent::SPACE_BETWEEN,
699 Align::Start => JustifyContent::FLEX_START,
700 };
701 style
702}
703
704#[cfg(test)]
705mod tests;
706
707pub(super) fn snap(v: f32) -> f32 {
709 (v + 0.5).floor()
710}
711
712#[derive(Clone, Copy, Debug, Default)]
718pub(super) struct Place {
719 pub(super) at: (f32, f32),
720 pub(super) raw: (f32, f32),
721}
722
723pub(super) fn placed(
729 tree: &TaffyTree<Option<Leaf>>,
730 id: NodeId,
731 parent: Place,
732 _root: bool,
733) -> (Frame, Place) {
734 let l = tree.unrounded_layout(id);
735 let base = parent.raw;
736 let (x, y) = (base.0 + l.location.x, base.1 + l.location.y);
737 let frame = Frame {
738 x: parent.at.0 + snap(x) - snap(base.0),
739 y: parent.at.1 + snap(y) - snap(base.1),
740 width: snap(x + l.size.width) - snap(x),
741 height: snap(y + l.size.height) - snap(y),
742 };
743 let place = Place {
744 at: (frame.x, frame.y),
745 raw: (x, y),
746 };
747 (frame, place)
748}