Keep allocation destinations readable without blocking collapsed header drops
This commit is contained in:
@@ -911,7 +911,7 @@ fn toolbar_status_leaves_transformed_top_edge_work_header_readable() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toolbar_status_leaves_transformed_top_edge_frozen_target_label_readable() {
|
||||
fn toolbar_destination_names_transformed_target_whose_header_is_clipped() {
|
||||
for zoom in [0.75, 1.25] {
|
||||
let mut f = fixture(Layer::Oa);
|
||||
let mut project = f.app.project().clone();
|
||||
@@ -940,11 +940,8 @@ fn toolbar_status_leaves_transformed_top_edge_frozen_target_label_readable() {
|
||||
let feedback = f.app.allocation_drag.feedback.as_ref().unwrap();
|
||||
assert!(!feedback.refused, "{}", feedback.text);
|
||||
let target = &feedback.press.frozen.targets[feedback.targets[0]];
|
||||
let top = screen(target.label.position).y - bounds().y;
|
||||
assert!(
|
||||
top > 0. && top < 14.,
|
||||
"label starts in the formerly obscured band: {top}"
|
||||
);
|
||||
assert!(screen(xy(target.region.x, target.region.y)).y < bounds().y);
|
||||
assert_eq!(feedback.destination_identity(), "A");
|
||||
assert!(f.app.allocation_drag.preview.is_some());
|
||||
let _ = f.app.update(release);
|
||||
assert_eq!(
|
||||
@@ -955,7 +952,8 @@ fn toolbar_status_leaves_transformed_top_edge_frozen_target_label_readable() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_source_or_history() {
|
||||
fn oa_sa_clipped_selected_header_refuses_preview_release_and_retries_without_partial_source_or_history()
|
||||
{
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for (zoom, pan) in [
|
||||
(1., Vector::ZERO),
|
||||
@@ -972,6 +970,7 @@ fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_sourc
|
||||
"Temporary".into(),
|
||||
)));
|
||||
let _ = f.app.update(Message::Undo);
|
||||
let pan = Vector::new(pan.x, pan.y - 100. * zoom);
|
||||
f.app.zoom = zoom;
|
||||
f.app.pan = pan;
|
||||
f.app.fit_to_view = false;
|
||||
@@ -982,7 +981,10 @@ fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_sourc
|
||||
)
|
||||
};
|
||||
let from = screen(xy(140., 535.));
|
||||
let overlap = screen(xy(160., 140.));
|
||||
// Keep the pointer inside the visible target even when the camera
|
||||
// clips its top. Only the grabbed work header must be clipped.
|
||||
let overlap = screen(xy(160., 110_f32.max(-pan.y / zoom + 5.)));
|
||||
assert!(bounds().contains(overlap));
|
||||
let clear = screen(xy(160., 190.));
|
||||
let accepted = f.app.project().clone();
|
||||
let source = f.app.document.bundle().clone();
|
||||
@@ -994,7 +996,7 @@ fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_sourc
|
||||
assert!(f.app.allocation_drag.preview.is_none());
|
||||
let feedback = f.app.allocation_drag.feedback.as_ref().unwrap();
|
||||
assert!(feedback.refused, "{}", feedback.text);
|
||||
assert!(feedback.text.contains("Aim below or beside"));
|
||||
assert!(feedback.text.contains("Make room or adjust the view"));
|
||||
assert_eq!(
|
||||
feedback.press.frozen.targets[feedback.targets[0]].element,
|
||||
f.a
|
||||
@@ -1014,7 +1016,7 @@ fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_sourc
|
||||
assert!(!f.app.document.can_undo());
|
||||
assert!(f.app.document.can_redo());
|
||||
|
||||
// In a new gesture, moving out of the collision reuses its semantic
|
||||
// In a new gesture, moving into the viewport reuses its semantic
|
||||
// candidate and stable allocation identity; only geometry changes.
|
||||
let mut events = batch(&f.app, from, overlap);
|
||||
events.pop();
|
||||
@@ -1074,3 +1076,115 @@ fn oa_sa_label_overlap_refuses_preview_release_and_retries_without_partial_sourc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oa_sa_toolbar_identity_accepts_exact_receiver_and_collapsed_header_drag() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
for (name, collapsed) in [("Receiver", false), ("Receiving subsystem", true)] {
|
||||
for (zoom, pan) in [
|
||||
(1., Vector::ZERO),
|
||||
(0.75, Vector::new(37., -15.)),
|
||||
(1.25, Vector::new(-30., 23.)),
|
||||
(1., Vector::new(0., -100.)),
|
||||
] {
|
||||
let mut f = fixture(layer);
|
||||
let mut project = f.app.project().clone();
|
||||
project.elements.get_mut(&f.a).unwrap().name = name.into();
|
||||
let target = project
|
||||
.diagrams
|
||||
.get_mut(&f.diagram)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.values_mut()
|
||||
.find(|n| n.element == f.a)
|
||||
.unwrap();
|
||||
target.collapsed = collapsed;
|
||||
let target_id = target.id;
|
||||
replace(&mut f, project);
|
||||
f.app.zoom = zoom;
|
||||
f.app.pan = pan;
|
||||
f.app.fit_to_view = false;
|
||||
let screen = |world: Position| {
|
||||
Point::new(
|
||||
bounds().x + world.x * zoom + pan.x,
|
||||
bounds().y + world.y * zoom + pan.y,
|
||||
)
|
||||
};
|
||||
let accepted = f.app.project().clone();
|
||||
let source = f.app.document.bundle().clone();
|
||||
let snapshot = f.app.document.snapshot();
|
||||
// Normal name grab (40,35), release inside the original header.
|
||||
let mut events = batch(&f.app, screen(xy(140., 535.)), screen(xy(160., 140.)));
|
||||
let release = events.pop().unwrap();
|
||||
send(&mut f.app, events);
|
||||
let feedback = f
|
||||
.app
|
||||
.allocation_drag
|
||||
.feedback
|
||||
.as_ref()
|
||||
.expect(&f.app.status);
|
||||
assert!(!feedback.refused, "{}", feedback.text);
|
||||
assert_eq!(feedback.destination_identity(), name);
|
||||
assert_eq!(
|
||||
feedback.press.frozen.targets[feedback.targets[0]].placement,
|
||||
target_id
|
||||
);
|
||||
let preview = f.app.allocation_drag.preview.as_ref().expect(&f.app.status);
|
||||
assert_eq!(
|
||||
preview.placement.diagram.nodes[&f.node].position,
|
||||
xy(120., 110.)
|
||||
);
|
||||
assert!(!preview.placement.diagram.nodes[&target_id].collapsed);
|
||||
assert_eq!(
|
||||
preview.placement.view,
|
||||
projection::project_diagram_with_index(
|
||||
preview.semantic.project(),
|
||||
&preview.placement.diagram,
|
||||
preview.semantic.index()
|
||||
)
|
||||
);
|
||||
let mut expected = preview.semantic.project().clone();
|
||||
expected
|
||||
.diagrams
|
||||
.insert(f.diagram, preview.placement.diagram.clone());
|
||||
assert_eq!(f.app.project(), &accepted);
|
||||
assert_eq!(f.app.document.bundle(), &source);
|
||||
assert!(Arc::ptr_eq(&snapshot, &f.app.document.snapshot()));
|
||||
assert!(!f.app.document.can_undo());
|
||||
let _ = f.app.update(release);
|
||||
assert_eq!(f.app.project(), &expected, "{}", f.app.status);
|
||||
assert_eq!(f.app.document.revision(), 1);
|
||||
assert_eq!(
|
||||
validation::effective_performer(f.app.project(), f.work),
|
||||
Some(f.a)
|
||||
);
|
||||
let after = f.app.document.bundle().clone();
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &accepted);
|
||||
assert_eq!(f.app.document.bundle(), &source);
|
||||
assert!(!f.app.document.can_undo());
|
||||
let _ = f.app.update(Message::Redo);
|
||||
assert_eq!(f.app.project(), &expected);
|
||||
assert_eq!(f.app.document.bundle(), &after);
|
||||
assert!(!f.app.document.can_redo());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn destination_identity_does_not_describe_blocked_or_outside_viewport_as_unallocation() {
|
||||
let f = fixture(Layer::Oa);
|
||||
let p = press(&f);
|
||||
let mut feedback = Feedback {
|
||||
press: p,
|
||||
targets: vec![],
|
||||
text: "A card blocks this drop".into(),
|
||||
refused: true,
|
||||
};
|
||||
assert_eq!(feedback.destination_identity(), "No eligible destination");
|
||||
feedback.text = "Release inside the captured canvas".into();
|
||||
assert_eq!(feedback.destination_identity(), "No eligible destination");
|
||||
feedback.refused = false;
|
||||
assert_eq!(feedback.destination_identity(), "Outside performers");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user