Keep allocation destinations readable without blocking collapsed header drops

This commit is contained in:
2026-09-12 04:34:02 -07:00
parent 76209cc186
commit 1b3c401a19
7 changed files with 416 additions and 157 deletions
+14 -68
View File
@@ -53,16 +53,7 @@ fn presentation(p: &Project, d: Id, target: Option<Id>) -> PresentationContext {
let target = target.map(|element| {
let view = projection::project_view(p, d);
let node = view.nodes.iter().find(|n| n.element == element).unwrap();
TargetPresentation {
placement: node.id,
label: Rect {
position: node.position,
size: Size {
width: node.size.width.min(224.),
height: 62.,
},
},
}
TargetPresentation { placement: node.id }
});
PresentationContext {
viewport: Rect {
@@ -1008,7 +999,7 @@ fn collapsed_target_unfold_is_explicit_and_no_other_fold_changes() {
}
#[test]
fn target_presentation_must_name_the_destination_and_its_visible_frozen_header() {
fn target_presentation_requires_accepted_identity_and_selected_header_visibility() {
let f = fixture(Layer::Oa);
let plan = semantic(&f, Destination::Performer(f.a));
assert!(matches!(
@@ -1016,13 +1007,13 @@ fn target_presentation_must_name_the_destination_and_its_visible_frozen_header()
Err(Refusal::LayoutRequired(_))
));
let mut context = presentation(&f.p, f.d, Some(f.a));
context.target.as_mut().unwrap().label.position = xy(0., 0.);
context.target.as_mut().unwrap().placement = f.n;
assert!(matches!(
place(&plan, xy(150., 200.), context),
Err(Refusal::LayoutRequired(_))
));
let mut clipped = presentation(&f.p, f.d, Some(f.a));
clipped.viewport.position.y = 180.;
clipped.viewport.position.y = 220.;
assert!(matches!(
place(&plan, xy(150., 200.), clipped),
Err(Refusal::LayoutRequired(_))
@@ -1030,78 +1021,34 @@ fn target_presentation_must_name_the_destination_and_its_visible_frozen_header()
}
#[test]
fn frozen_performer_label_refuses_overlapping_work_header_and_allows_nearby_exact_move() {
fn toolbar_identity_allows_exact_incoming_placement_at_old_label_overlap() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = fixture(layer);
f.p.elements.get_mut(&f.a).unwrap().name = "Receiver".into();
let plan = semantic(&f, Destination::Performer(f.a));
let mut context = presentation(&f.p, f.d, Some(f.a));
// The actual frozen renderer bounds for Receiver at (100, 100).
context.target.as_mut().unwrap().label = Rect {
position: xy(110., 131.),
size: Size {
width: 88.,
height: 22.,
},
};
let label = context.target.unwrap().label;
let counterexample = Rect {
position: xy(120., 110.),
size: Size {
width: 224.,
height: 62.,
},
};
assert!(context.viewport.contains(label));
assert!(context.viewport.contains(counterexample));
assert!(
label.contains(Rect {
position: xy(134., 145.),
size: Size {
width: 1.,
height: 1.
},
}),
"the formerly admitted work name begins underneath the label"
);
let context = presentation(&f.p, f.d, Some(f.a));
let before = plan.project().clone();
assert!(matches!(
place(&plan, xy(120., 105.), context),
Err(Refusal::LayoutRequired(message)) if message.contains("Aim below or beside")
));
let placed = place(&plan, xy(120., 105.), context).unwrap();
assert_eq!(placed.diagram.nodes[&f.n].position, xy(120., 110.));
assert_eq!(plan.project(), &before);
// A 50-unit lower pointer position clears the frozen label. The planner
// preserves the user's snapped position instead of packing either card.
let placed = place(&plan, xy(120., 155.), context).unwrap();
assert_eq!(placed.diagram.nodes[&f.n].position, xy(120., 160.));
let after = roundtrip(&f.p, finish(&plan, &f.p, &placed).unwrap().unwrap());
assert_eq!(validation::effective_performer(&after, f.w), Some(f.a));
assert_eq!(after.diagrams[&f.d].nodes[&f.n].position, xy(120., 160.));
assert_eq!(after.diagrams[&f.d].nodes[&f.n].position, xy(120., 110.));
assert_eq!(after.elements, f.p.elements);
assert_eq!(after.diagrams[&f.second], f.p.diagrams[&f.second]);
}
}
#[test]
fn frozen_label_readability_allows_touching_edges_but_refuses_positive_area_overlap() {
fn selected_header_visibility_allows_viewport_edge_but_refuses_clipping() {
let f = fixture(Layer::Oa);
let plan = semantic(&f, Destination::Performer(f.a));
let mut context = presentation(&f.p, f.d, Some(f.a));
context.target.as_mut().unwrap().label = Rect {
position: xy(110., 131.),
size: Size {
width: 90.,
height: 19.,
},
};
context.viewport.position.y = 100.;
for (position, allowed) in [
(xy(190., 110.), false),
(xy(200., 110.), true),
(xy(210., 110.), true),
(xy(120., 140.), false),
(xy(120., 150.), true),
(xy(120., 160.), true),
(xy(120., 90.), false),
(xy(120., 100.), true),
(xy(120., 110.), true),
] {
let result = place(&plan, position, context);
assert_eq!(result.is_ok(), allowed, "{position:?}: {result:?}");
@@ -1110,7 +1057,6 @@ fn frozen_label_readability_allows_touching_edges_but_refuses_positive_area_over
}
}
}
#[test]
fn same_performer_readability_preserves_snapped_noop_and_allocation_identity() {
for layer in [Layer::Oa, Layer::Sa] {