1
0
Fork 0
forked from wry/wry

Run planned multiphase layout animations

This commit is contained in:
atagen 2026-05-21 18:37:00 +10:00
parent b50e8d5683
commit 13722429b4
2 changed files with 274 additions and 9 deletions

View file

@ -4,7 +4,9 @@ use {
allocator::BufferObject,
animation::{
AnimationCurve, AnimationState, AnimationTick, RetainedExitLayer, RetainedToplevel,
expand_damage_rect, spawn_in_start_rect,
expand_damage_rect,
multiphase::{MultiphaseRequest, MultiphaseWindow, plan_no_overlap},
spawn_in_start_rect,
},
async_engine::{AsyncEngine, SpawnedFuture},
backend::{
@ -157,6 +159,7 @@ use {
uapi::{OwnedFd, c},
};
#[derive(Clone)]
pub(crate) struct LayoutAnimationCandidate {
node_id: NodeId,
old: Rect,
@ -1583,11 +1586,98 @@ impl State {
return;
};
let now = self.now_nsec();
if self.start_multiphase_layout_animation(&candidates, now) {
return;
}
for candidate in candidates {
self.start_layout_animation_candidate(candidate, now);
}
}
fn start_multiphase_layout_animation(
self: &Rc<Self>,
candidates: &[LayoutAnimationCandidate],
now_nsec: u64,
) -> bool {
if candidates.len() < 2 {
return false;
}
let windows: Vec<_> = candidates
.iter()
.map(|candidate| MultiphaseWindow {
node_id: candidate.node_id,
from: self
.animations
.visual_rect(candidate.node_id, candidate.old, now_nsec),
to: candidate.new,
})
.collect();
let Some(first) = windows.first() else {
return false;
};
let mut bounds = first.from.union(first.to);
for window in &windows[1..] {
bounds = bounds.union(window.from).union(window.to);
}
let Ok(plan) = plan_no_overlap(&MultiphaseRequest { bounds, windows }) else {
return false;
};
if plan.phases.is_empty() {
return false;
}
let mut entries = vec![];
for candidate in candidates {
let mut current =
self.animations
.visual_rect(candidate.node_id, candidate.old, now_nsec);
let mut damage = current.union(candidate.new);
let mut phases = vec![];
for phase in &plan.phases {
match phase
.steps
.iter()
.find(|step| step.node_id == candidate.node_id)
{
Some(step) => {
phases.push((step.from, step.to));
damage = damage.union(step.from).union(step.to);
current = step.to;
}
None => phases.push((current, current)),
}
}
if current != candidate.new {
return false;
}
let retained = self
.animations
.retained_snapshot(candidate.node_id, now_nsec)
.or_else(|| candidate.retained.clone());
entries.push((candidate.clone(), phases, damage, retained));
}
let mut started_any = false;
for (candidate, phases, damage, retained) in entries {
if self.animations.set_phased_target(
candidate.node_id,
phases,
retained,
now_nsec,
self.animations.duration_ms.get(),
candidate.curve,
) {
started_any = true;
self.damage(expand_damage_rect(
damage,
self.theme.sizes.border_width.get().max(0),
));
}
}
if started_any {
self.ensure_animation_tick();
}
started_any
}
pub fn queue_spawn_in_animation(
self: &Rc<Self>,
node_id: NodeId,