diff --git a/crates/oa-desktop/src/behavior/scenario_layout.rs b/crates/oa-desktop/src/behavior/scenario_layout.rs index d1d1e238..fa160196 100644 --- a/crates/oa-desktop/src/behavior/scenario_layout.rs +++ b/crates/oa-desktop/src/behavior/scenario_layout.rs @@ -348,12 +348,14 @@ impl Plan { if covered.y < rect.y { return Err(GeometryIssue::FixedFrameHeaderWouldMove { frame: id }); } - if covered.y + covered.height > rect.y + rect.height { - let old_covered = old_coverage[&id]; - let padding = - (original.y + original.height - old_covered.y - old_covered.height).max(0.); - rect.height = covered.y + covered.height + padding - rect.y; - } + let old_covered = old_coverage[&id]; + let padding = + (original.y + original.height - old_covered.y - old_covered.height).max(0.); + // Preserve padding before the footprint itself reaches the old + // frame edge: operand scopes already retain their own padding. + rect.height = rect + .height + .max(covered.y + covered.height + padding - rect.y); } frames.insert(id, rect); drawn.insert(id, rect); diff --git a/crates/oa-desktop/src/behavior/scenario_layout/tests.rs b/crates/oa-desktop/src/behavior/scenario_layout/tests.rs index d5ea1b28..97137e4f 100644 --- a/crates/oa-desktop/src/behavior/scenario_layout/tests.rs +++ b/crates/oa-desktop/src/behavior/scenario_layout/tests.rs @@ -1050,6 +1050,19 @@ fn post_fragment_death_cannot_disappear_and_feasible_operand_spacing_retains_its let (document, owner, _, second, delete, right) = alternatives_with_death(); let mut p = document.project().clone(); let baseline = Scene::sequence(&p, owner, owner); + let operand = p.elements[&second].parent.unwrap(); + let fragment = p.elements[&operand].parent.unwrap(); + let baseline_frame = baseline + .boxes + .iter() + .find(|item| item.id == fragment) + .unwrap() + .rect; + assert_eq!( + (baseline_frame.y, baseline_frame.y + baseline_frame.height), + (148., 567.) + ); + assert_eq!(baseline.sequence_bounds[&operand], (433., 525.)); let x = baseline.render_lanes[&right]; assert_eq!(baseline.rows[&delete].1, 631.); assert_eq!( @@ -1064,6 +1077,23 @@ fn post_fragment_death_cannot_disappear_and_feasible_operand_spacing_retains_its ); let view = saved(&mut p, owner, Some(positions(&[(second, 520.)], &[]))); let resolved = result(&p, view); + // The message still lies above the old frame bottom, but its padded + // operand would end at 570. Retain the automatic 92px frame padding. + assert_eq!(resolved.scene.rows[&second].1, 520.); + assert_eq!(resolved.plan.scopes[&operand], (433., 570.)); + let frame = resolved.plan.frames[&fragment]; + assert_eq!((frame.y, frame.y + frame.height), (148., 612.)); + assert_eq!(resolved.scene.sequence_bounds[&operand], (433., 570.)); + assert_eq!( + resolved + .scene + .boxes + .iter() + .find(|item| item.id == fragment) + .unwrap() + .rect, + frame + ); assert!(resolved.scene.lifecycle.missing_geometry().is_empty()); assert_eq!( resolved.scene.lifecycle.marker_at(Point::new(x, 631.)),