Run planned multiphase layout animations
This commit is contained in:
parent
b50e8d5683
commit
13722429b4
2 changed files with 274 additions and 9 deletions
92
src/state.rs
92
src/state.rs
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue