Bridge interrupted phased retargets
This commit is contained in:
parent
e7f9a5cb09
commit
09305ab026
1 changed files with 213 additions and 3 deletions
216
src/state.rs
216
src/state.rs
|
|
@ -7,7 +7,8 @@ use {
|
|||
RetainedToplevel,
|
||||
expand_damage_rect,
|
||||
multiphase::{
|
||||
MultiphaseRequest, MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||
MultiphasePhase, MultiphasePlan, MultiphasePlanFailure, MultiphaseRequest,
|
||||
MultiphaseWindow, MultiphaseWindowHierarchy,
|
||||
partition_motion_groups, plan_no_overlap_with_diagnostics, validate_phase_paths,
|
||||
},
|
||||
spawn_in_start_rect,
|
||||
|
|
@ -205,6 +206,56 @@ fn layout_animation_group_uses_plain(
|
|||
.any(|&idx| candidates[idx].style == AnimationStyle::Plain)
|
||||
}
|
||||
|
||||
fn bridged_retarget_plan(
|
||||
request: &MultiphaseRequest,
|
||||
candidates: &[LayoutAnimationCandidate],
|
||||
group: &[usize],
|
||||
bridge_paths: &[Vec<(Rect, Rect)>],
|
||||
bridge_phase_count: usize,
|
||||
follow_phases: &[MultiphasePhase],
|
||||
) -> Result<MultiphasePlan, MultiphasePlanFailure> {
|
||||
let mut paths = vec![];
|
||||
for (group_pos, &idx) in group.iter().enumerate() {
|
||||
let candidate = &candidates[idx];
|
||||
let window = request.windows[group_pos];
|
||||
let Some(bridge_path) = bridge_paths.get(group_pos) else {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
};
|
||||
let mut path = bridge_path.clone();
|
||||
let mut current = path
|
||||
.last()
|
||||
.map(|(_, to)| *to)
|
||||
.unwrap_or(window.from);
|
||||
while path.len() < bridge_phase_count {
|
||||
path.push((current, current));
|
||||
}
|
||||
if current != candidate.old {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
for phase in follow_phases {
|
||||
match phase
|
||||
.steps
|
||||
.iter()
|
||||
.find(|step| step.node_id == candidate.node_id)
|
||||
{
|
||||
Some(step) => {
|
||||
if step.from != current {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
path.push((step.from, step.to));
|
||||
current = step.to;
|
||||
}
|
||||
None => path.push((current, current)),
|
||||
}
|
||||
}
|
||||
if current != window.to {
|
||||
return Err(MultiphasePlanFailure::NoPattern);
|
||||
}
|
||||
paths.push(path);
|
||||
}
|
||||
validate_phase_paths(request, &paths)
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub pid: c::pid_t,
|
||||
pub kb_ctx: KbvmContext,
|
||||
|
|
@ -1721,6 +1772,9 @@ impl State {
|
|||
if self.start_existing_phased_retarget(candidates, windows, group, &request, now_nsec) {
|
||||
return true;
|
||||
}
|
||||
if self.start_bridged_phased_retarget(candidates, windows, group, &request, now_nsec) {
|
||||
return true;
|
||||
}
|
||||
let plan = match plan_no_overlap_with_diagnostics(&request) {
|
||||
Ok(plan) => plan,
|
||||
Err(diagnostic) => {
|
||||
|
|
@ -1770,6 +1824,97 @@ impl State {
|
|||
self.start_multiphase_plan(candidates, windows, group, &plan.phases, now_nsec)
|
||||
}
|
||||
|
||||
fn start_bridged_phased_retarget(
|
||||
self: &Rc<Self>,
|
||||
candidates: &[LayoutAnimationCandidate],
|
||||
windows: &[MultiphaseWindow],
|
||||
group: &[usize],
|
||||
request: &MultiphaseRequest,
|
||||
now_nsec: u64,
|
||||
) -> bool {
|
||||
let mut bridge_paths = vec![];
|
||||
let mut bridge_phase_count = 0;
|
||||
let mut has_bridge = false;
|
||||
for &idx in group {
|
||||
let candidate = &candidates[idx];
|
||||
let window = windows[idx];
|
||||
if window.from == candidate.old {
|
||||
bridge_paths.push(vec![]);
|
||||
continue;
|
||||
}
|
||||
let Some(path) =
|
||||
self.animations
|
||||
.phased_route_to(candidate.node_id, candidate.old, now_nsec)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if !path.is_empty() {
|
||||
has_bridge = true;
|
||||
bridge_phase_count = bridge_phase_count.max(path.len());
|
||||
}
|
||||
bridge_paths.push(path);
|
||||
}
|
||||
if !has_bridge {
|
||||
return false;
|
||||
}
|
||||
|
||||
let settled_windows: Vec<_> = group
|
||||
.iter()
|
||||
.map(|&idx| {
|
||||
let candidate = &candidates[idx];
|
||||
MultiphaseWindow::with_hierarchy(
|
||||
candidate.node_id,
|
||||
candidate.old,
|
||||
candidate.new,
|
||||
candidate.hierarchy,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let Some(first) = settled_windows.first() else {
|
||||
return false;
|
||||
};
|
||||
let mut bounds = first.from.union(first.to);
|
||||
for window in &settled_windows[1..] {
|
||||
bounds = bounds.union(window.from).union(window.to);
|
||||
}
|
||||
let settled_request = MultiphaseRequest {
|
||||
bounds,
|
||||
windows: settled_windows,
|
||||
clearance: self.layout_animation_clearance(),
|
||||
};
|
||||
let follow_plan = match plan_no_overlap_with_diagnostics(&settled_request) {
|
||||
Ok(plan) => plan,
|
||||
Err(diagnostic) => {
|
||||
log::debug!(
|
||||
"bridged phased retarget follow-up rejected for group {:?}: {:?}",
|
||||
group,
|
||||
diagnostic
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let plan = match bridged_retarget_plan(
|
||||
request,
|
||||
candidates,
|
||||
group,
|
||||
&bridge_paths,
|
||||
bridge_phase_count,
|
||||
&follow_plan.phases,
|
||||
) {
|
||||
Ok(plan) => plan,
|
||||
Err(error) => {
|
||||
log::debug!(
|
||||
"bridged phased retarget rejected for group {:?}: {:?}",
|
||||
group,
|
||||
error
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
log::debug!("bridging active phased animation for group {:?}", group);
|
||||
self.start_multiphase_plan(candidates, windows, group, &plan.phases, now_nsec)
|
||||
}
|
||||
|
||||
fn start_multiphase_plan(
|
||||
self: &Rc<Self>,
|
||||
candidates: &[LayoutAnimationCandidate],
|
||||
|
|
@ -2512,10 +2657,24 @@ mod tests {
|
|||
}
|
||||
|
||||
fn candidate(node_id: u32, style: AnimationStyle) -> LayoutAnimationCandidate {
|
||||
candidate_rects(
|
||||
node_id,
|
||||
rect(0, 0, 100, 100),
|
||||
rect(100, 0, 200, 100),
|
||||
style,
|
||||
)
|
||||
}
|
||||
|
||||
fn candidate_rects(
|
||||
node_id: u32,
|
||||
old: Rect,
|
||||
new: Rect,
|
||||
style: AnimationStyle,
|
||||
) -> LayoutAnimationCandidate {
|
||||
LayoutAnimationCandidate {
|
||||
node_id: NodeId(node_id),
|
||||
old: rect(0, 0, 100, 100),
|
||||
new: rect(100, 0, 200, 100),
|
||||
old,
|
||||
new,
|
||||
curve: AnimationCurve::Linear,
|
||||
style,
|
||||
hierarchy: MultiphaseWindowHierarchy::default(),
|
||||
|
|
@ -2533,6 +2692,57 @@ mod tests {
|
|||
assert!(layout_animation_group_uses_plain(&candidates, &[0, 1]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridged_retarget_handles_second_rotation_interrupt() {
|
||||
let a_left = rect(0, 0, 100, 100);
|
||||
let c_mid = rect(100, 0, 200, 100);
|
||||
let c_left = a_left;
|
||||
let a_mid = c_mid;
|
||||
let c_current = rect(150, 50, 250, 100);
|
||||
let c_mid_lane = rect(100, 50, 200, 100);
|
||||
let candidates = vec![
|
||||
candidate_rects(1, a_left, a_mid, AnimationStyle::Multiphase),
|
||||
candidate_rects(3, c_mid, c_left, AnimationStyle::Multiphase),
|
||||
];
|
||||
let request = MultiphaseRequest {
|
||||
bounds: rect(0, 0, 250, 100),
|
||||
windows: vec![
|
||||
MultiphaseWindow::new(NodeId(1), a_left, a_mid),
|
||||
MultiphaseWindow::new(NodeId(3), c_current, c_left),
|
||||
],
|
||||
clearance: 0,
|
||||
};
|
||||
let settled_request = MultiphaseRequest {
|
||||
bounds: rect(0, 0, 200, 100),
|
||||
windows: vec![
|
||||
MultiphaseWindow::new(NodeId(1), a_left, a_mid),
|
||||
MultiphaseWindow::new(NodeId(3), c_mid, c_left),
|
||||
],
|
||||
clearance: 0,
|
||||
};
|
||||
let follow_plan = plan_no_overlap_with_diagnostics(&settled_request).unwrap();
|
||||
let bridge_paths = vec![vec![], vec![(c_current, c_mid_lane), (c_mid_lane, c_mid)]];
|
||||
|
||||
let plan = bridged_retarget_plan(
|
||||
&request,
|
||||
&candidates,
|
||||
&[0, 1],
|
||||
&bridge_paths,
|
||||
2,
|
||||
&follow_plan.phases,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(plan
|
||||
.phases
|
||||
.iter()
|
||||
.any(|phase| phase.steps.iter().any(|step| step.node_id == NodeId(1))));
|
||||
assert!(plan
|
||||
.phases
|
||||
.iter()
|
||||
.any(|phase| phase.steps.iter().any(|step| step.node_id == NodeId(3))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layout_animation_candidates_coalesce_duplicate_nodes() {
|
||||
let source = MultiphaseHierarchyPosition {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue