use std::collections::{BTreeMap, BTreeSet};
use crate::prefix_sums::{Item, Summary, Sums};
use crate::scene::{Align, Layout, Node};
use crate::tree::{Order, OrderKey};
use super::*;
mod fill;
mod free;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(in crate::render) enum Owner {
Top,
Run(u32),
}
pub(in crate::render) fn is_run(node: &Node) -> bool {
!leafy(node) && !node.children.is_empty()
}
pub(in crate::render) struct Child {
pub(in crate::render) wrap: NodeId,
pub(in crate::render) node: NodeId,
pub(in crate::render) stretch: bool,
pub(in crate::render) sens: bool,
last: Option<Shape>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct Shape {
room: AvailableSpace,
least: bool,
line: Option<f32>,
basis: Option<f32>,
main: Option<f32>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(in crate::render) struct Ask {
line: Option<f32>,
room: AvailableSpace,
least: bool,
main: Option<f32>,
avail: Option<f32>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(in crate::render) struct Sized {
pub(in crate::render) along: f32,
pub(in crate::render) across: f32,
pub(in crate::render) own: f32,
pub(in crate::render) first: f32,
}
#[derive(Clone, Default, Debug)]
pub(in crate::render) struct Length(pub(in crate::render) f64);
impl Summary for Length {
fn then(&self, next: &Self) -> Self {
Length(self.0 + next.0)
}
}
impl Item for Sized {
type Sum = Length;
fn summary(&self) -> Length {
Length(self.along as f64)
}
}
pub(in crate::render) struct Asked {
ask: Ask,
pub(in crate::render) line: f32,
sizes: Sums<OrderKey, Sized>,
owns: BTreeMap<u32, usize>,
flexed: Option<f32>,
}
const ASKS: usize = 4;
pub(in crate::render) struct RunLayout {
pub(in crate::render) tree: TaffyTree<Option<Leaf>>,
pub(in crate::render) order: Order,
pub(in crate::render) owner: Owner,
pub(in crate::render) leaf: NodeId,
pub(in crate::render) inside: Option<OrderKey>,
pub(in crate::render) row: bool,
pub(in crate::render) free: bool,
pub(in crate::render) cb: (f32, f32),
pub(in crate::render) hug: (bool, bool),
boxes: BTreeMap<OrderKey, [f32; 4]>,
rights: BTreeMap<u32, usize>,
bottoms: BTreeMap<u32, usize>,
pub(in crate::render) padding: [f32; 4],
pub(in crate::render) gap: f32,
pub(in crate::render) justify: Align,
pub(in crate::render) align: Align,
pub(in crate::render) children: BTreeMap<OrderKey, Child>,
stale: BTreeSet<OrderKey>,
asked: Vec<Asked>,
base: Sums<OrderKey, Sized>,
base_owns: BTreeMap<u32, usize>,
stretched: BTreeSet<OrderKey>,
overs: BTreeSet<OrderKey>,
pub(in crate::render) relaid: BTreeSet<OrderKey>,
fills: BTreeMap<OrderKey, NodeId>,
flex: TaffyTree<Option<Leaf>>,
flexer: NodeId,
spacer: NodeId,
gone: bool,
}
#[derive(Default)]
pub(in crate::render) struct Runs {
slots: Vec<Option<RunLayout>>,
free: Vec<u32>,
}
impl Runs {
pub(in crate::render) fn reserve(&mut self) -> u32 {
match self.free.pop() {
Some(id) => id,
None => {
self.slots.push(None);
(self.slots.len() - 1) as u32
}
}
}
pub(in crate::render) fn put(&mut self, id: u32, run: RunLayout) {
self.slots[id as usize] = Some(run);
}
pub(in crate::render) fn take(&mut self, id: u32) -> RunLayout {
self.slots[id as usize].take().expect("a run in its slot")
}
pub(in crate::render) fn get(&self, id: u32) -> &RunLayout {
self.slots[id as usize].as_ref().expect("a run in its slot")
}
pub(in crate::render) fn get_mut(&mut self, id: u32) -> &mut RunLayout {
self.slots[id as usize].as_mut().expect("a run in its slot")
}
pub(in crate::render) fn free(&mut self, id: u32) {
self.slots[id as usize] = None;
self.free.push(id);
}
pub(in crate::render) fn touch(
&mut self,
top: &mut TaffyTree<Option<Leaf>>,
id: u32,
key: OrderKey,
) {
let mut at = (id, key);
loop {
let run = self.get_mut(at.0);
run.stale.insert(at.1.clone());
if let Some(c) = run.children.get_mut(&at.1) {
c.last = None;
}
let (owner, leaf, inside) = (run.owner, run.leaf, run.inside.clone());
match owner {
Owner::Top => {
top.mark_dirty(leaf).expect("leaf");
return;
}
Owner::Run(p) => {
self.get_mut(p).tree.mark_dirty(leaf).expect("leaf");
match inside {
Some(k) => at = (p, k),
None => return,
}
}
}
}
}
}
impl RunLayout {
pub(in crate::render) fn new(
node: &Node,
order: Order,
owner: Owner,
leaf: NodeId,
inside: Option<OrderKey>,
) -> RunLayout {
let mut tree = TaffyTree::new();
tree.disable_rounding();
let mut flex = TaffyTree::new();
flex.disable_rounding();
let spacer = flex
.new_leaf(Style {
flex_shrink: 0.0,
..Style::default()
})
.expect("leaf");
let flexer = flex
.new_with_children(Style::default(), &[spacer])
.expect("node");
let mut run = RunLayout {
tree,
order,
owner,
leaf,
inside,
row: true,
free: node.layout == Layout::None,
cb: (0.0, 0.0),
hug: (false, false),
boxes: BTreeMap::new(),
rights: BTreeMap::new(),
bottoms: BTreeMap::new(),
padding: [0.0; 4],
gap: 0.0,
justify: Align::Start,
align: Align::Start,
children: BTreeMap::new(),
stale: BTreeSet::new(),
asked: Vec::new(),
base: Sums::new(),
base_owns: BTreeMap::new(),
stretched: BTreeSet::new(),
overs: BTreeSet::new(),
relaid: BTreeSet::new(),
gone: false,
fills: BTreeMap::new(),
flex,
flexer,
spacer,
};
run.set(node);
run
}
pub(in crate::render) fn set(&mut self, node: &Node) -> bool {
let row = node.layout == Layout::Row;
let changed = row != self.row;
if node.gap != self.gap || node.padding != self.padding || node.align != self.align {
self.gone = true;
}
self.row = row;
self.padding = node.padding;
self.gap = node.gap;
self.justify = node.justify;
self.align = node.align;
if changed {
self.asked.clear();
self.base = Sums::new();
self.base_owns.clear();
let keys: Vec<OrderKey> = self.children.keys().cloned().collect();
for k in keys {
self.children.get_mut(&k).expect("a child").last = None;
self.stale.insert(k);
}
let dir = if row {
FlexDirection::Row
} else {
FlexDirection::Column
};
for c in self.children.values() {
let mut style = self.tree.style(c.wrap).expect("style").clone();
style.flex_direction = dir;
self.tree.set_style(c.wrap, style).expect("style");
}
}
changed
}
pub(in crate::render) fn classify(&mut self, key: &OrderKey, stretch: bool, sens: bool) {
let Some(c) = self.children.get_mut(key) else {
return;
};
if (c.stretch, c.sens) == (stretch, sens) {
return;
}
c.stretch = stretch;
c.sens = sens;
c.last = None;
self.stale.insert(key.clone());
}
pub(in crate::render) fn size_of(&self, a: &Asked, key: &OrderKey) -> Option<Sized> {
if self.overs.contains(key) {
a.sizes.get(key).copied()
} else {
self.base.get(key).copied()
}
}
pub(in crate::render) fn add(
&mut self,
key: OrderKey,
node: NodeId,
stretch: bool,
sens: bool,
) {
let wrap = self
.tree
.new_with_children(self.wrap_style(), &[node])
.expect("node");
self.children.insert(
key.clone(),
Child {
wrap,
node,
stretch,
sens,
last: None,
},
);
self.stale.insert(key);
}
pub(in crate::render) fn add_fill(&mut self, key: OrderKey, style: Style) {
let proxy = self
.flex
.new_leaf_with_context(style, Some(Leaf::Proxy(key.clone())))
.expect("leaf");
self.flex.add_child(self.flexer, proxy).expect("child");
self.fills.insert(key.clone(), proxy);
if let Some(c) = self.children.get_mut(&key) {
c.last = None;
}
self.stale.insert(key);
}
pub(in crate::render) fn remove(&mut self, key: &OrderKey) -> Option<Child> {
if let Some(proxy) = self.fills.remove(key) {
self.flex.remove_child(self.flexer, proxy).expect("child");
let _ = self.flex.remove(proxy);
}
let c = self.children.remove(key)?;
self.tree.remove_child(c.wrap, c.node).expect("child");
let _ = self.tree.remove(c.wrap);
self.stale.remove(key);
self.relaid.remove(key);
self.forget(key);
self.gone = true;
Some(c)
}
fn forget(&mut self, key: &OrderKey) {
if let Some(b) = self.boxes.remove(key) {
uncount(&mut self.rights, b[0] + b[2]);
uncount(&mut self.bottoms, b[1] + b[3]);
}
if let Some(s) = self.base.remove(key) {
uncount(&mut self.base_owns, s.own);
}
for a in &mut self.asked {
if let Some(s) = a.sizes.remove(key) {
uncount(&mut a.owns, s.own);
}
}
self.stretched.remove(key);
self.overs.remove(key);
}
fn wrap_style(&self) -> Style {
if self.free {
return Style {
position: Position::Relative,
..Style::default()
};
}
Style {
flex_direction: if self.row {
FlexDirection::Row
} else {
FlexDirection::Column
},
align_items: AlignItems::FLEX_START,
..Style::default()
}
}
pub(in crate::render) fn padding_along(&self) -> (f32, f32) {
let [t, r, b, l] = self.padding;
if self.row { (l, r) } else { (t, b) }
}
pub(in crate::render) fn padding_across(&self) -> (f32, f32) {
let [t, r, b, l] = self.padding;
if self.row { (t, b) } else { (l, r) }
}
pub(in crate::render) fn placed(&self, line: f32) -> Option<&Asked> {
let near = |a: &&Asked| !a.ask.least && (a.line - line).abs() < 1e-4;
self.asked
.iter()
.rev()
.filter(near)
.find(|a| a.ask.line.is_some())
.or_else(|| self.asked.iter().rev().find(near))
}
pub(in crate::render) fn across(&self, line: f32, size: &Sized, stretch: bool) -> f32 {
if stretch || (line - size.across).abs() < 1e-3 {
return 0.0;
}
match self.align {
Align::Center => (line - size.across) / 2.0,
Align::End => line - size.across,
_ => 0.0,
}
}
pub(in crate::render) fn lead(&self, free: f32) -> f32 {
if free.abs() < 1e-3 {
return 0.0;
}
match self.justify {
Align::Center => free / 2.0,
Align::End => free,
_ => 0.0,
}
}
pub(in crate::render) fn length(&self, asked: &Asked) -> f64 {
let n = self.children.len();
self.base.total().0 + asked.sizes.total().0 + self.gap as f64 * n.saturating_sub(1) as f64
}
fn broadest(&self, a: &Asked) -> f32 {
greatest(&self.base_owns).max(greatest(&a.owns))
}
}
fn own_key(v: f32) -> u32 {
v.max(0.0).to_bits()
}
fn count(owns: &mut BTreeMap<u32, usize>, v: f32) {
*owns.entry(own_key(v)).or_default() += 1;
}
fn uncount(owns: &mut BTreeMap<u32, usize>, v: f32) {
let k = own_key(v);
if let Some(n) = owns.get_mut(&k) {
*n -= 1;
if *n == 0 {
owns.remove(&k);
}
}
}
fn greatest(owns: &BTreeMap<u32, usize>) -> f32 {
owns.keys().next_back().map_or(0.0, |k| f32::from_bits(*k))
}
impl Renderer {
pub(in crate::render) fn measure_run(
&mut self,
runs: &mut Runs,
id: u32,
known: Size<Option<f32>>,
available: Size<AvailableSpace>,
) -> Size<f32> {
if runs.get(id).free {
return self.measure_free(runs, id, known);
}
let mut run = runs.take(id);
let (pa0, pa1) = run.padding_along();
let (pc0, pc1) = run.padding_across();
let (known_along, known_across) = if run.row {
(known.width, known.height)
} else {
(known.height, known.width)
};
let (room_along, room_across) = if run.row {
(available.width, available.height)
} else {
(available.height, available.width)
};
let known_across = known_across.map(|c| c.max(pc0 + pc1));
let line = known_across.map(|c| (c - pc0 - pc1).max(0.0));
let room = match line {
Some(l) => AvailableSpace::Definite(l),
None => match room_across {
AvailableSpace::Definite(v) => AvailableSpace::Definite((v - pc0 - pc1).max(0.0)),
other => other,
},
};
let (main, avail) = if run.fills.is_empty() {
(None, None)
} else {
(
known_along.map(|m| (m - pa0 - pa1).max(0.0)),
match room_along {
AvailableSpace::Definite(v) => Some((v - pa0 - pa1).max(0.0)),
_ => None,
},
)
};
let ask = Ask {
line,
room,
least: room_along == AvailableSpace::MinContent,
main,
avail,
};
self.refresh(runs, &mut run, ask);
let at = match run.asked.iter().position(|a| a.ask == ask) {
Some(i) => {
let a = run.asked.remove(i);
run.asked.push(a);
run.asked.len() - 1
}
None => {
let a = self.ask(runs, &mut run, ask);
if run.asked.len() == ASKS {
run.asked.remove(0);
}
run.asked.push(a);
run.asked.len() - 1
}
};
let a = &run.asked[at];
let along =
known_along.unwrap_or_else(|| pa0 + pa1 + a.flexed.unwrap_or(run.length(a) as f32));
let across = known_across.unwrap_or(pc0 + pc1 + a.line);
let row = run.row;
runs.put(id, run);
if row {
Size {
width: along,
height: across,
}
} else {
Size {
width: across,
height: along,
}
}
}
fn ask(&mut self, runs: &mut Runs, run: &mut RunLayout, ask: Ask) -> Asked {
let mut a = Asked {
ask,
line: ask.line.unwrap_or(0.0),
sizes: Sums::new(),
owns: BTreeMap::new(),
flexed: None,
};
let keys: Vec<OrderKey> = run
.overs
.iter()
.filter(|k| !run.fills.contains_key(*k))
.cloned()
.collect();
for k in &keys {
let s = self.child(runs, run, k, ask, ask.line, None);
run.relaid.insert(k.clone());
count(&mut a.owns, s.own);
a.sizes.insert(k.clone(), s);
}
self.settle_line(runs, run, &mut a, None);
a
}
fn settle_line(
&mut self,
runs: &mut Runs,
run: &mut RunLayout,
a: &mut Asked,
stale: Option<&[OrderKey]>,
) {
let was = a.line;
if !run.fills.is_empty() {
self.flex(runs, run, a);
} else if a.ask.line.is_none() {
a.line = run.broadest(a);
}
if a.ask.line.is_none() {
let keys: Vec<OrderKey> = match stale {
Some(s) if a.line == was => s.to_vec(),
_ => run.stretched.iter().cloned().collect(),
};
self.across_line(runs, run, a, &keys);
}
}
fn across_line(
&mut self,
runs: &mut Runs,
run: &mut RunLayout,
a: &mut Asked,
keys: &[OrderKey],
) {
for k in keys {
if !run.stretched.contains(k) || run.fills.contains_key(k) {
continue;
}
let Some(first) = a.sizes.get(k).copied() else {
continue;
};
let basis = Some(first.first);
let mut s = self.child(runs, run, k, a.ask, Some(a.line), basis);
run.relaid.insert(k.clone());
s.own = first.own;
s.first = first.first;
a.sizes.insert(k.clone(), s);
}
}
pub(in crate::render) fn relay(
&mut self,
runs: &mut Runs,
rid: u32,
key: &OrderKey,
line: f32,
) {
let mut run = runs.take(rid);
if !run.overs.contains(key) {
runs.put(rid, run);
return;
}
if run.fills.contains_key(key)
&& let Some(a) = run.placed(line)
&& let Some(s) = a.sizes.get(key).copied()
{
let (ask, stretch) = (a.ask, run.children[key].stretch);
self.fill_child(runs, &mut run, key, ask, s.along, stretch.then_some(line));
runs.put(rid, run);
return;
}
if let Some(a) = run.placed(line)
&& let Some(s) = a.sizes.get(key).copied()
{
let ask = a.ask;
if ask.line.is_none() && run.children[key].stretch {
self.child(runs, &mut run, key, ask, Some(line), Some(s.first));
} else {
self.child(runs, &mut run, key, ask, ask.line, None);
}
}
runs.put(rid, run);
}
fn refresh(&mut self, runs: &mut Runs, run: &mut RunLayout, now: Ask) {
let stale: Vec<OrderKey> = std::mem::take(&mut run.stale)
.into_iter()
.filter(|k| run.children.contains_key(k))
.collect();
let gone = std::mem::take(&mut run.gone);
if stale.is_empty() && !gone {
return;
}
let mut asked = std::mem::take(&mut run.asked);
for a in &mut asked {
for k in &stale {
if let Some(old) = a.sizes.remove(k) {
uncount(&mut a.owns, old.own);
}
}
}
for k in &stale {
if let Some(old) = run.base.remove(k) {
uncount(&mut run.base_owns, old.own);
}
let c = &run.children[k];
let (stretch, over) = (c.stretch, c.stretch || c.sens);
if stretch {
run.stretched.insert(k.clone());
} else {
run.stretched.remove(k);
}
run.relaid.insert(k.clone());
if let Some(&proxy) = run.fills.get(k) {
run.flex.mark_dirty(proxy).expect("proxy");
run.overs.insert(k.clone());
} else if over {
run.overs.insert(k.clone());
for a in &mut asked {
let s = self.child(runs, run, k, a.ask, a.ask.line, None);
count(&mut a.owns, s.own);
a.sizes.insert(k.clone(), s);
}
} else {
run.overs.remove(k);
let s = self.child(runs, run, k, now, now.line, None);
count(&mut run.base_owns, s.own);
run.base.insert(k.clone(), s);
}
}
for a in &mut asked {
self.settle_line(runs, run, a, Some(&stale));
}
run.asked = asked;
}
fn child(
&mut self,
runs: &mut Runs,
run: &mut RunLayout,
key: &OrderKey,
ask: Ask,
line: Option<f32>,
basis: Option<f32>,
) -> Sized {
let shape = Shape {
room: ask.room,
least: ask.least,
line,
basis,
main: None,
};
let (wrap, node, last) = {
let c = &run.children[key];
(c.wrap, c.node, c.last)
};
if last == Some(shape) {
return self.sized(run, node);
}
let mut own = run.tree.style(node).expect("style").clone();
let want = basis.map_or(Dimension::auto(), Dimension::length);
if own.flex_basis != want {
own.flex_basis = want;
run.tree.set_style(node, own).expect("style");
}
let mut style = run.tree.style(wrap).expect("style").clone();
let across = line.map_or(Dimension::auto(), Dimension::length);
let changed = if run.row {
std::mem::replace(&mut style.size.height, across) != across
} else {
std::mem::replace(&mut style.size.width, across) != across
};
if changed {
run.tree.set_style(wrap, style).expect("style");
}
let along = if ask.least {
AvailableSpace::MinContent
} else {
AvailableSpace::MaxContent
};
let room = match line {
Some(l) => AvailableSpace::Definite(l),
None => ask.room,
};
let available = if run.row {
Size {
width: along,
height: room,
}
} else {
Size {
width: room,
height: along,
}
};
self.settle_in(runs, &mut run.tree, wrap, available, &[]);
run.children.get_mut(key).expect("a child").last = Some(shape);
self.sized(run, node)
}
fn sized(&self, run: &RunLayout, node: NodeId) -> Sized {
let l = run.tree.unrounded_layout(node).size;
let (along, across) = if run.row {
(l.width, l.height)
} else {
(l.height, l.width)
};
Sized {
along,
across,
own: across,
first: along,
}
}
}