Keep allocation destinations readable without blocking collapsed header drops
This commit is contained in:
@@ -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] {
|
||||
|
||||
Reference in New Issue
Block a user