use std::collections::{BTreeSet, HashMap};
use kurbo::{Rect as KRect, Vec2};
use crate::display::{Order, Space};
use crate::scene::{Node, Scene};
use crate::tree::OrderKey;
use super::*;
impl Renderer {
pub(super) fn update(
&mut self,
d: &mut Drawing,
new: &Scene,
applied: &crate::scene::Applied,
) -> Result<Damage, Undrawable> {
self.register(&new.fonts);
d.visited = 0;
let changes = Changes::of(d, new, applied);
let mut damage = Vec::new();
let mut all = false;
let mut images = false;
for order in &changes.removed {
images |= self.unplace(d, new, order, &mut damage);
}
for order in &changes.added {
self.place(d, new, order);
}
for order in &changes.attrs {
restyle(d, new, order);
}
d.laid.settle_top(self);
let mut path: HashMap<Order, BTreeSet<OrderKey>> = HashMap::new();
for order in changes.touched() {
for k in 0..order.len() {
path.entry(order[..k].to_vec())
.or_default()
.insert(order[k].clone());
}
}
let attrs: BTreeSet<Order> = changes.attrs.iter().cloned().collect();
let dirty = |o: &Order| attrs.contains(o);
let placing: BTreeSet<Order> = applied
.changed
.iter()
.filter_map(|(id, old)| {
let o = new.ids.get(id)?;
let n = new.at(o)?;
let moves = old.layout != n.layout
|| old.gap != n.gap
|| old.padding != n.padding
|| old.align != n.align
|| old.justify != n.justify
|| old.width != n.width
|| old.height != n.height
|| old.clip != n.clip
|| old.radius != n.radius;
moves.then(|| o.clone())
})
.collect();
let relaid = |o: &Order| placing.contains(o);
self.blobs = std::mem::take(&mut d.blobs);
let (t, c) = (d.list.root_transform, d.list.root_clip);
let mut walk = Walk {
scene: new,
dirty: &dirty,
relaid: &relaid,
path: Some(&path),
damage: Some(&mut damage),
fresh: None,
images: false,
};
let root = d.laid.root;
self.sync(
d,
&mut walk,
&new.root,
Vec::new(),
Owner::Top,
root,
t,
Place::default(),
(&Space::Root, Vec2::ZERO),
c,
None,
None,
(false, false),
None,
);
d.list.flush();
images |= walk.images;
d.blobs = std::mem::take(&mut self.blobs);
let area = d.list.area;
self.settle_area(d);
let now = d.list.area;
if now != area {
if (now.x, now.y) != (area.x, area.y) {
all = true;
} else {
let (w0, w1) = (area.width.min(now.width), area.width.max(now.width));
let (h0, h1) = (area.height.min(now.height), area.height.max(now.height));
let (x, y) = (now.x as f64, now.y as f64);
if w0 < w1 {
damage.push(KRect::new(x + w0 as f64, y, x + w1 as f64, y + h1 as f64));
}
if h0 < h1 {
damage.push(KRect::new(x, y + h0 as f64, x + w1 as f64, y + h1 as f64));
}
}
}
if images {
self.settle_images(d)?;
} else {
d.fits.clone()?;
}
Ok(if all {
Damage::All
} else {
Damage::Rects {
origin: (now.x as f64, now.y as f64),
rects: damage,
}
})
}
}
#[derive(Default, Debug)]
pub(super) struct Changes {
pub(super) attrs: Vec<Order>,
pub(super) added: Vec<Order>,
pub(super) removed: Vec<Order>,
}
impl Changes {
pub(super) fn of(d: &Drawing, new: &Scene, applied: &crate::scene::Applied) -> Changes {
let mut removed: BTreeSet<Order> = applied
.removed
.iter()
.filter_map(|id| d.orders.get(id).cloned())
.collect();
let mut added: BTreeSet<Order> = applied
.placed
.iter()
.filter_map(|id| new.ids.get(id).cloned())
.collect();
let mut attrs = BTreeSet::new();
let under =
|set: &BTreeSet<Order>, o: &[OrderKey]| (0..=o.len()).any(|k| set.contains(&o[..k]));
for (id, old) in &applied.changed {
let Some(o) = new.ids.get(id) else { continue };
if !d.orders.get(id).is_some_and(|was| was == o) || under(&added, o) {
continue;
}
let node = new.at(o).expect("indexed");
let free = |n: &Node| n.layout == crate::scene::Layout::None;
if leafy(old) != leafy(node) || (free(old) != free(node) && !node.children.is_empty()) {
removed.insert(o.clone());
added.insert(o.clone());
continue;
}
attrs.insert(o.clone());
if old.layout != node.layout {
for k in node.children.entries().map(|(k, _)| k) {
let mut c = o.clone();
c.push(k.clone());
attrs.insert(c);
}
}
}
let mut maybe: BTreeSet<Order> = BTreeSet::new();
let along = |n: &Node, c: &Node| match n.layout {
crate::scene::Layout::Row => c.width,
crate::scene::Layout::Column => c.height,
crate::scene::Layout::None => crate::scene::Size::Hug,
};
let fill = crate::scene::Size::Fill;
for (id, old) in &applied.changed {
let Some(o) = new.ids.get(id) else { continue };
let Some(node) = new.at(o) else { continue };
if old.layout != node.layout || old.justify != node.justify {
maybe.insert(o.clone());
}
if !o.is_empty()
&& let Some(p) = new.at(&o[..o.len() - 1])
&& ((along(p, old) == fill) != (along(p, node) == fill))
&& !under(&added, o)
{
removed.insert(o.clone());
added.insert(o.clone());
}
}
for o in added.iter().chain(&removed) {
if !o.is_empty() {
maybe.insert(o[..o.len() - 1].to_vec());
}
}
for p in maybe {
let Some(n) = new.at(&p) else { continue };
if d.orders.get(&n.id) != Some(&p) || under(&added, &p) {
continue;
}
let was = d.laid.run_of.contains_key(&p);
let now = if was
&& !n.children.is_empty()
&& n.justify != crate::scene::Align::SpaceBetween
&& n.layout != crate::scene::Layout::None
&& added
.iter()
.filter(|o| o.len() == p.len() + 1 && o.starts_with(&p))
.all(|o| new.at(o).is_none_or(|c| along(n, c) != fill))
&& !applied.changed.iter().any(|(id, _)| {
new.ids.get(id).is_some_and(|o| {
o.len() == p.len() + 1
&& o.starts_with(&p)
&& new.at(o).is_some_and(|c| along(n, c) == fill)
})
}) {
true
} else {
is_run(n)
};
if was != now {
removed.insert(p.clone());
added.insert(p);
}
}
let tops = |set: &BTreeSet<Order>| -> Vec<Order> {
set.iter()
.filter(|o| !(0..o.len()).any(|k| set.contains(&o[..k])))
.cloned()
.collect()
};
let removed = tops(&removed);
let added = tops(&added);
let attrs = attrs
.into_iter()
.filter(|o| !under(&added.iter().cloned().collect(), o))
.collect();
Changes {
attrs,
added,
removed,
}
}
pub(super) fn touched(&self) -> impl Iterator<Item = &[OrderKey]> {
self.attrs
.iter()
.chain(&self.added)
.map(|o| o.as_slice())
.chain(
self.removed
.iter()
.filter(|o| !o.is_empty())
.map(|o| &o[..o.len() - 1]),
)
}
}
impl Drawing {
pub(super) fn forget(&mut self, order: &Order, damage: &mut Vec<KRect>) -> bool {
let under: Vec<Order> = self
.recs
.range(order.clone()..)
.take_while(|(o, _)| o.starts_with(order))
.map(|(o, _)| o.clone())
.collect();
let mut images = false;
for o in under.iter().rev() {
let at = self.list.chunk(o).map(|c| self.list.space_at(&c.space));
if let Some(c) = self.list.take(o) {
let at = at.unwrap_or(Vec2::ZERO);
damage.extend(c.bounds.map(|b| b + at));
self.list.free(&c.made);
}
if let Some(rid) = self.laid.run_of.get(o)
&& let Some(drid) = self.shown.remove(rid)
{
self.list.remove_run(drid);
self.shifts.remove(&drid);
self.contexts.remove(&drid);
}
if let Some(rec) = self.recs.remove(o) {
if self.orders.get(&rec.node) == Some(o) {
self.orders.remove(&rec.node);
}
images |= !rec.painted.is_empty();
self.painters.remove(o);
self.list.trees.transforms.remove(rec.transform);
if let Some(e) = rec.effect {
self.list.trees.effects.remove(e);
}
if let Some(c) = rec.clip {
self.list.trees.clips.remove(c);
}
}
self.frames.remove(o);
}
images
}
pub(super) fn inside(&self, owner: Owner, order: &Order) -> Option<OrderKey> {
match owner {
Owner::Top => None,
Owner::Run(p) => {
let at = self.laid.runs.get(p).order.len();
order.get(at).cloned()
}
}
}
}
impl Renderer {
fn unplace(
&mut self,
d: &mut Drawing,
new: &Scene,
order: &Order,
damage: &mut Vec<KRect>,
) -> bool {
let parent = (!order.is_empty()).then(|| order[..order.len() - 1].to_vec());
let run = parent.as_ref().and_then(|p| d.laid.run_of.get(p).copied());
let key = order.last().cloned();
if !order.is_empty() {
d.laid.sens_drop(&new.root, order);
}
if let (Some(rid), Some(k)) = (run, &key)
&& let Some(&drid) = d.shown.get(&rid)
&& let Some(at) = d.list.run(drid).child_at(k)
{
let r = d.list.run(drid);
let abs = d.list.space_at(&r.space) + at;
let along = if r.row { abs.x } else { abs.y };
let s = d.shifts.entry(drid).or_insert(along);
*s = s.min(along);
}
let images = d.forget(order, damage);
if let (Some(rid), Some(k)) = (run, &key)
&& let Some(&drid) = d.shown.get(&rid)
{
d.list.place_child(drid, k.clone(), None);
}
if !d.laid.nodes.contains_key(order) {
return images;
}
let nodes: Vec<Order> = d
.laid
.nodes
.range(order.clone()..)
.take_while(|(o, _)| o.starts_with(order))
.map(|(o, _)| o.clone())
.collect();
let mut freed = Vec::new();
for o in &nodes {
d.laid.nodes.remove(o);
if let Some(rid) = d.laid.run_of.remove(o) {
freed.push(rid);
}
}
if order.is_empty() {
for rid in freed {
d.laid.runs.free(rid);
}
return images;
}
match (run, key) {
(Some(rid), Some(k)) => {
let r = d.laid.runs.get_mut(rid);
if let Some(c) = r.remove(&k) {
remove_subtree(&mut r.tree, c.node);
}
let laid = &mut d.laid;
laid.runs.touch(&mut laid.top, rid, k);
}
_ => unreachable!("a child taken from a frame that isn't a run"),
}
for rid in freed {
d.laid.runs.free(rid);
}
images
}
fn place(&mut self, d: &mut Drawing, new: &Scene, order: &Order) {
let node = find_node(&new.root, order);
if order.is_empty() {
d.laid = Laid::build(self, new);
return;
}
let parent_order = order[..order.len() - 1].to_vec();
let parent = find_node(&new.root, &parent_order);
let key = order.last().expect("a key").clone();
let (w, h) = d.laid.sens_build(node, order.clone());
if w || h {
d.laid
.sens_settle(&new.root, &parent_order, (w as isize, h as isize));
}
if let Some(&rid) = d.laid.run_of.get(&parent_order) {
let mut run = d.laid.runs.take(rid);
d.laid.add_child(
&mut run,
rid,
&key,
node,
parent,
&new.images,
&parent_order,
);
d.laid.runs.put(rid, run);
let laid = &mut d.laid;
laid.runs.touch(&mut laid.top, rid, key);
return;
}
unreachable!("a child placed under a frame that isn't a run");
}
}
fn restyle(d: &mut Drawing, new: &Scene, order: &Order) {
let node = find_node(&new.root, order);
let parent = (!order.is_empty()).then(|| find_node(&new.root, &order[..order.len() - 1]));
let Some(&(owner, id)) = d.laid.nodes.get(order) else {
return;
};
let style = style_of(node, parent);
d.laid.sens_settle(&new.root, order, (0, 0));
if let Some(&rid) = d.laid.run_of.get(order) {
let run = d.laid.runs.get_mut(rid);
run.set(node);
run.hug = hugs_by_extent(node, parent).unwrap_or((false, false));
}
let tree = d.laid.tree_mut(owner);
tree.set_style(id, style).expect("style");
if let Some(leaf) = leaf_of(node, &new.images) {
tree.set_node_context(id, Some(Some(leaf))).expect("leaf");
}
if let Some(p) = parent
&& let Some(&rid) = d.laid.run_of.get(&order[..order.len() - 1])
{
let k = order.last().expect("a key").clone();
let row = d.laid.runs.get(rid).row;
let across = if row { node.height } else { node.width };
let sens = d.laid.sensitive(order);
d.laid
.runs
.get_mut(rid)
.classify(&k, across == crate::scene::Size::Fill, sens);
let _ = p;
let laid = &mut d.laid;
laid.runs.touch(&mut laid.top, rid, k);
return;
}
if let Some(k) = d.inside(owner, order)
&& let Owner::Run(p) = owner
{
let laid = &mut d.laid;
laid.runs.touch(&mut laid.top, p, k);
}
}
#[cfg(test)]
mod costs {
mod tests;
}
#[cfg(test)]
mod tests;