1
0
Fork 0
forked from wry/wry

Repair animation integration paths

This commit is contained in:
atagen 2026-05-22 09:16:51 +10:00
parent 31c289f628
commit 0fefe814c3
9 changed files with 229 additions and 35 deletions

View file

@ -152,6 +152,7 @@ pub enum PlanStrategy {
SingleAction,
MixedSinglePhase,
HierarchyOrderedScales,
OrientationChange { from_axis: PhaseAxis },
SwapLanes { axis: PhaseAxis },
SpaceThenOrthogonalGrowth { axis: PhaseAxis },
ReversedForwardPlan { original: Box<PlanStrategy> },
@ -501,6 +502,22 @@ fn plan_forward(
}
}
}
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
match plan_orientation_change(request, axis) {
Ok(plan) => return Ok(plan),
Err(error) => {
record_rejection(
&mut attempted,
direction,
PlanStrategy::OrientationChange { from_axis: axis },
error,
);
if error != MultiphasePlanFailure::NoPattern {
rejection.get_or_insert(error);
}
}
}
}
for axis in [PhaseAxis::Horizontal, PhaseAxis::Vertical] {
match plan_axis_crossing_lanes(request, axis) {
Ok(plan) => return Ok(plan),
@ -920,6 +937,94 @@ fn plan_space_then_orthogonal_growth(
)
}
fn plan_orientation_change(
request: &MultiphaseRequest,
from_axis: PhaseAxis,
) -> Result<MultiphasePlanned, MultiphasePlanFailure> {
if request.windows.len() < 2 {
return Err(MultiphasePlanFailure::NoPattern);
}
let to_axis = from_axis.other();
let min_lane_size = sane_min_size(main_size(request.bounds, to_axis));
let target_start = request
.windows
.first()
.map(|window| main_start(window.to, from_axis))
.ok_or(MultiphasePlanFailure::NoPattern)?;
let target_end = request
.windows
.first()
.map(|window| main_end(window.to, from_axis))
.ok_or(MultiphasePlanFailure::NoPattern)?;
let source_start = request
.windows
.first()
.map(|window| main_start(window.from, to_axis))
.ok_or(MultiphasePlanFailure::NoPattern)?;
let source_end = request
.windows
.first()
.map(|window| main_end(window.from, to_axis))
.ok_or(MultiphasePlanFailure::NoPattern)?;
if request.windows.iter().any(|window| {
main_start(window.from, to_axis) != source_start
|| main_end(window.from, to_axis) != source_end
|| main_start(window.to, from_axis) != target_start
|| main_end(window.to, from_axis) != target_end
|| main_size(window.to, to_axis) < min_lane_size
}) {
return Err(MultiphasePlanFailure::NoPattern);
}
let mut phase1 = vec![];
let mut phase2 = vec![];
let mut phase3 = vec![];
for window in &request.windows {
let lane = with_main_interval(
window.from,
to_axis,
main_start(window.to, to_axis),
main_end(window.to, to_axis),
);
let moved = with_main_interval(
lane,
from_axis,
main_start(window.to, from_axis),
main_start(window.to, from_axis) + main_size(lane, from_axis),
);
push_step(&mut phase1, window.node_id, window.from, lane);
push_step(&mut phase2, window.node_id, lane, moved);
push_step(&mut phase3, window.node_id, moved, window.to);
}
if phase1.is_empty() || phase3.is_empty() {
return Err(MultiphasePlanFailure::NoPattern);
}
build_validated_plan(
request,
PlanStrategy::OrientationChange { from_axis },
[
phase_draft(
PhaseKind::Scale,
to_axis,
phase1,
PhaseReason::ShrinkIntoLanes { lane_axis: to_axis },
),
phase_draft(
PhaseKind::Move,
from_axis,
phase2,
PhaseReason::MoveThroughFreedSpace,
),
phase_draft(
PhaseKind::Scale,
from_axis,
phase3,
PhaseReason::GrowOutOfLanes,
),
],
)
}
struct MultiphasePhaseDraft {
action: MultiphasePhaseActionDraft,
steps: Vec<MultiphaseStep>,
@ -1665,6 +1770,20 @@ mod tests {
strategy: PlanStrategy::HierarchyOrderedScales,
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::OrientationChange {
from_axis: PhaseAxis::Horizontal,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::OrientationChange {
from_axis: PhaseAxis::Vertical,
},
reason: MultiphasePlanFailure::NoPattern,
},
RejectedStrategy {
direction,
strategy: PlanStrategy::SwapLanes {
@ -2024,6 +2143,46 @@ mod tests {
assert!(validate_plan_continuous(&req, plan));
}
#[test]
fn orientation_change_shrinks_moves_then_grows() {
let req = request(vec![
window(1, rect(0, 0, 200, 400), rect(0, 0, 400, 200)),
window(2, rect(200, 0, 400, 400), rect(0, 200, 400, 400)),
]);
let planned = plan_no_overlap_explained(&req).unwrap();
let plan = &planned.plan;
assert_eq!(
planned.explanation.strategy,
PlanStrategy::OrientationChange {
from_axis: PhaseAxis::Horizontal,
}
);
assert_eq!(
actions(plan),
vec![
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Vertical,
},
PhaseAction {
kind: PhaseKind::Move,
axis: PhaseAxis::Horizontal,
},
PhaseAction {
kind: PhaseKind::Scale,
axis: PhaseAxis::Horizontal,
},
]
);
assert_eq!(step_to(plan, 0, id(1)), rect(0, 0, 200, 200));
assert_eq!(step_to(plan, 0, id(2)), rect(200, 200, 400, 400));
assert_eq!(step_to(plan, 1, id(2)), rect(0, 200, 200, 400));
assert_eq!(step_to(plan, 2, id(1)), rect(0, 0, 400, 200));
assert_eq!(step_to(plan, 2, id(2)), rect(0, 200, 400, 400));
assert!(validate_plan_continuous(&req, plan));
}
#[test]
fn two_axis_redistribution_without_hierarchy_still_falls_back() {
let req = request(vec![