Preserve the desktop-2 captured source (1286 passed, 6 existing ignored). Three menu payload blobs are staged from that verified capture without replacing working files; the strict-lint size correction follows in a separate commit. Native acceptance remains pending.
657 lines
25 KiB
Rust
657 lines
25 KiB
Rust
// Included in state_canvas::tests: exercise the real canvas event path.
|
|
|
|
fn bend_fixture(authored: bool) -> (Fixture, Id) {
|
|
let mut f = fixture();
|
|
let relation = f.project.relate(
|
|
RelationKind::StateTransition,
|
|
"Standby to stopped",
|
|
f.b,
|
|
f.c,
|
|
);
|
|
if authored {
|
|
f.project
|
|
.diagrams
|
|
.get_mut(&f.diagram)
|
|
.unwrap()
|
|
.routes
|
|
.push(EdgeRoute {
|
|
edge: relation,
|
|
waypoints: vec![Position {
|
|
x: 650.25,
|
|
y: 340.75,
|
|
}],
|
|
source_attachment: Some(EdgeAttachment {
|
|
side: PortAnchorSide::Bottom,
|
|
offset: 0.5,
|
|
}),
|
|
target_attachment: Some(EdgeAttachment {
|
|
side: PortAnchorSide::Top,
|
|
offset: 0.25,
|
|
}),
|
|
label: Some(EdgeLabel {
|
|
fraction: 0.43,
|
|
offset: Position { x: 28., y: -16. },
|
|
}),
|
|
});
|
|
}
|
|
validate_candidate(&f.project).unwrap();
|
|
(f, relation)
|
|
}
|
|
|
|
fn bend_segment(scene: &Scene, relation: Id) -> (usize, Point) {
|
|
let edge = &scene.routed.edges[&relation];
|
|
edge.points
|
|
.windows(2)
|
|
.enumerate()
|
|
.filter(|(i, _)| *i > 0 && *i + 2 < edge.points.len())
|
|
.map(|(i, p)| {
|
|
(
|
|
i,
|
|
Point::new((p[0].x + p[1].x) / 2., (p[0].y + p[1].y) / 2.),
|
|
(p[1].x - p[0].x).abs() + (p[1].y - p[0].y).abs(),
|
|
)
|
|
})
|
|
.filter(|(_, p, length)| {
|
|
*length > 40. && edge.pins.iter().all(|pin| p.distance(point(*pin)) > 20.)
|
|
})
|
|
.max_by(|a, b| a.2.total_cmp(&b.2))
|
|
.map(|(i, p, _)| (i, p))
|
|
.expect("fixture has an exposed interior route segment")
|
|
}
|
|
|
|
fn bend_press(canvas: &StateCanvas<'_>, state: &mut State, at: Point) {
|
|
motion(canvas, state, at, at);
|
|
let event = input(
|
|
canvas,
|
|
state,
|
|
mouse::Event::ButtonPressed(mouse::Button::Left),
|
|
at,
|
|
);
|
|
assert!(
|
|
!matches!(event, Some(Event::SetBends { .. })),
|
|
"press alone cannot commit"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_segment_drag_inserts_one_pin_using_ordered_pointer_at_three_zooms() {
|
|
for zoom in [0.5, 1., 2.] {
|
|
let (f, relation) = bend_fixture(false);
|
|
let mut canvas = canvas(&f, Tool::Select, zoom);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let baseline = canvas.scene(&state).unwrap();
|
|
let (_, midpoint) = bend_segment(&baseline, relation);
|
|
let start = screen(&canvas, midpoint);
|
|
let destination = Position {
|
|
x: ((midpoint.x + 45.) / 5.).round() * 5.,
|
|
y: ((midpoint.y + 30.) / 5.).round() * 5.,
|
|
};
|
|
let release = screen(&canvas, point(destination));
|
|
let later_batch_cursor = release + Vector::new(137., 83.);
|
|
motion(&canvas, &mut state, start, later_batch_cursor);
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonPressed(mouse::Button::Left),
|
|
later_batch_cursor,
|
|
);
|
|
assert!(f.project.diagrams[&f.diagram].routes.is_empty());
|
|
motion(&canvas, &mut state, release, later_batch_cursor);
|
|
assert_eq!(
|
|
state.bend_preview.as_ref().unwrap().waypoints,
|
|
vec![destination]
|
|
);
|
|
assert_eq!(
|
|
canvas.scene(&state).unwrap().routed.edges[&relation].pins,
|
|
vec![destination]
|
|
);
|
|
let event = input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
later_batch_cursor,
|
|
)
|
|
.unwrap();
|
|
assert!(
|
|
matches!(&event, Event::SetBends { context, relation: id, waypoints }
|
|
if *context == canvas.context && *id == relation && *waypoints == vec![destination])
|
|
);
|
|
assert!(state.bend_preview.is_none());
|
|
assert!(
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
later_batch_cursor
|
|
)
|
|
.is_none()
|
|
);
|
|
let after = prepare_command(
|
|
&f.project,
|
|
&state_edit::plan(&f.project, canvas.context, &event)
|
|
.unwrap()
|
|
.command,
|
|
)
|
|
.unwrap()
|
|
.project;
|
|
let stored = &after.diagrams[&f.diagram].routes[0];
|
|
assert_eq!(stored.waypoints, vec![destination]);
|
|
assert!(
|
|
stored.source_attachment.is_none()
|
|
&& stored.target_attachment.is_none()
|
|
&& stored.label.is_none()
|
|
);
|
|
assert_eq!(after.relations, f.project.relations);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_authored_pin_move_preserves_full_project_and_exact_undo_redo() {
|
|
for zoom in [0.5, 1., 2.] {
|
|
let (mut f, relation) = bend_fixture(true);
|
|
let mut other = f.project.diagrams[&f.diagram].clone();
|
|
other.id = Id::new_v4();
|
|
other.alias = "bend_reference_view".into();
|
|
other.nodes = other
|
|
.nodes
|
|
.into_values()
|
|
.map(|mut node| {
|
|
node.id = Id::new_v4();
|
|
node.alias = format!("bend_reference_{}", node.alias);
|
|
(node.id, node)
|
|
})
|
|
.collect();
|
|
let other_id = other.id;
|
|
f.project.diagrams.insert(other_id, other.clone());
|
|
validate_candidate(&f.project).unwrap();
|
|
let original = f.project.diagrams[&f.diagram].routes[0].clone();
|
|
let mut canvas = canvas(&f, Tool::Select, zoom);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let start = screen(&canvas, point(original.waypoints[0]));
|
|
let destination = Position { x: 690., y: 365. };
|
|
let release = screen(&canvas, point(destination));
|
|
bend_press(&canvas, &mut state, start);
|
|
motion(&canvas, &mut state, release, release);
|
|
let preview = state.bend_preview.as_ref().unwrap();
|
|
let mut intended = original.clone();
|
|
intended.waypoints[0] = destination;
|
|
assert_eq!(preview, &intended);
|
|
assert_eq!(
|
|
f.project.diagrams[&f.diagram].routes[0], original,
|
|
"preview is transient"
|
|
);
|
|
let preview_scene = canvas.scene(&state).unwrap();
|
|
let event = input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
release,
|
|
)
|
|
.unwrap();
|
|
assert!(
|
|
matches!(&event, Event::SetBends { waypoints, .. } if waypoints == &intended.waypoints)
|
|
);
|
|
let plan = state_edit::plan(&f.project, canvas.context, &event).unwrap();
|
|
let mut expected = f.project.clone();
|
|
expected.diagrams.get_mut(&f.diagram).unwrap().routes[0] = intended;
|
|
let mut document = Document::from_project(f.project.clone(), Default::default()).unwrap();
|
|
let before = document.bundle().clone();
|
|
document.execute(plan.command).unwrap();
|
|
assert_eq!(document.project(), &expected);
|
|
assert_eq!(document.project().diagrams[&other_id], other);
|
|
let committed_view = projection::project_view(document.project(), f.diagram);
|
|
let committed = routing::route_diagram(
|
|
document.project(),
|
|
&document.project().diagrams[&f.diagram],
|
|
&committed_view,
|
|
);
|
|
let preview_edge = &preview_scene.routed.edges[&relation];
|
|
let committed_edge = &committed.edges[&relation];
|
|
assert_eq!(preview_edge.points, committed_edge.points);
|
|
assert_eq!(preview_edge.pin_fractions, committed_edge.pin_fractions);
|
|
assert_eq!(preview_edge.warnings, committed_edge.warnings);
|
|
let edited = document.bundle().clone();
|
|
assert!(document.undo());
|
|
assert_eq!(document.project(), &f.project);
|
|
assert_eq!(document.bundle(), &before);
|
|
assert!(!document.undo(), "one drag adds one history operation");
|
|
assert!(document.redo());
|
|
assert_eq!(document.project(), &expected);
|
|
assert_eq!(document.bundle(), &edited);
|
|
assert!(!document.redo());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_stationary_and_out_and_back_restore_off_grid_or_automatic_route() {
|
|
for authored in [false, true] {
|
|
for zoom in [0.5, 1., 2.] {
|
|
for travel in [false, true] {
|
|
let (f, relation) = bend_fixture(authored);
|
|
let mut canvas = canvas(&f, Tool::Select, zoom);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let scene = canvas.scene(&state).unwrap();
|
|
let world = if authored {
|
|
point(scene.routed.edges[&relation].pins[0])
|
|
} else {
|
|
bend_segment(&scene, relation).1
|
|
};
|
|
let start = screen(&canvas, world);
|
|
bend_press(&canvas, &mut state, start);
|
|
if travel {
|
|
let away = start + Vector::new(45., 30.) * zoom;
|
|
motion(&canvas, &mut state, away, away);
|
|
assert!(state.bend_preview.is_some());
|
|
motion(&canvas, &mut state, start, start);
|
|
}
|
|
assert!(
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
start
|
|
)
|
|
.is_none()
|
|
);
|
|
assert!(state.bend_preview.is_none());
|
|
assert_eq!(
|
|
canvas.scene(&state).unwrap().routed.edges[&relation].points,
|
|
scene.routed.edges[&relation].points
|
|
);
|
|
assert_eq!(
|
|
f.project.diagrams[&f.diagram].routes.len(),
|
|
usize::from(authored)
|
|
);
|
|
if authored {
|
|
assert_eq!(
|
|
f.project.diagrams[&f.diagram].routes[0].waypoints,
|
|
vec![Position {
|
|
x: 650.25,
|
|
y: 340.75
|
|
}]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_cancel_on_camera_context_focus_and_pointer_interruptions() {
|
|
for cancellation in 0..16 {
|
|
let (f, relation) = bend_fixture(true);
|
|
let mut canvas = canvas(&f, Tool::Select, 1.);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let original = f.project.diagrams[&f.diagram].routes[0].clone();
|
|
let start = screen(&canvas, point(original.waypoints[0]));
|
|
let release = start + Vector::new(45., 30.);
|
|
bend_press(&canvas, &mut state, start);
|
|
motion(&canvas, &mut state, release, release);
|
|
assert!(state.bend_preview.is_some(), "case {cancellation}");
|
|
let mut release_bounds = bounds();
|
|
match cancellation {
|
|
0 => canvas.zoom = 2.,
|
|
1 => canvas.pan.x += 10.,
|
|
2 => canvas.context.epoch += 1,
|
|
3 => canvas.context.revision += 1,
|
|
4 => canvas.context.document = Id::new_v4(),
|
|
5 => canvas.tool = Tool::Transition,
|
|
6 => canvas.editable = false,
|
|
7 | 8 => {
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonPressed(if cancellation == 7 {
|
|
mouse::Button::Right
|
|
} else {
|
|
mouse::Button::Middle
|
|
}),
|
|
release,
|
|
);
|
|
}
|
|
9 => {
|
|
canvas::Program::update(
|
|
&canvas,
|
|
&mut state,
|
|
&canvas::Event::Window(iced::window::Event::Unfocused),
|
|
bounds(),
|
|
mouse::Cursor::Available(release),
|
|
);
|
|
}
|
|
10 => {
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::WheelScrolled {
|
|
delta: mouse::ScrollDelta::Lines { x: 0., y: 1. },
|
|
},
|
|
release,
|
|
);
|
|
}
|
|
11 => {
|
|
input(&canvas, &mut state, mouse::Event::CursorLeft, release);
|
|
}
|
|
12 => {
|
|
motion(&canvas, &mut state, Point::new(-100., -100.), release);
|
|
}
|
|
13 => release_bounds.x += 25.,
|
|
15 => canvas.selected = f.b,
|
|
_ => {
|
|
let escape = canvas::Event::Keyboard(keyboard::Event::KeyPressed {
|
|
key: keyboard::Key::Named(keyboard::key::Named::Escape),
|
|
modified_key: keyboard::Key::Named(keyboard::key::Named::Escape),
|
|
physical_key: keyboard::key::Physical::Code(keyboard::key::Code::Escape),
|
|
location: keyboard::Location::Standard,
|
|
modifiers: keyboard::Modifiers::empty(),
|
|
text: None,
|
|
repeat: false,
|
|
});
|
|
let event = canvas::Program::update(
|
|
&canvas,
|
|
&mut state,
|
|
&escape,
|
|
bounds(),
|
|
mouse::Cursor::Available(release),
|
|
)
|
|
.and_then(|action| action.into_inner().0);
|
|
assert!(matches!(event, Some(Event::Choose(Tool::Select))));
|
|
}
|
|
}
|
|
let event = input_at_bounds(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
release,
|
|
release_bounds,
|
|
);
|
|
assert!(
|
|
!matches!(event, Some(Event::SetBends { .. })),
|
|
"late release committed case {cancellation}"
|
|
);
|
|
assert!(
|
|
state.bend_preview.is_none(),
|
|
"stale preview survived case {cancellation}"
|
|
);
|
|
assert_eq!(f.project.diagrams[&f.diagram].routes[0], original);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_context_menu_distinguishes_authored_pin_from_computed_segment() {
|
|
let (f, relation) = bend_fixture(true);
|
|
let mut canvas = canvas(&f, Tool::Select, 1.);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let scene = canvas.scene(&state).unwrap();
|
|
let pin = screen(&canvas, point(scene.routed.edges[&relation].pins[0]));
|
|
motion(&canvas, &mut state, pin, pin);
|
|
assert!(
|
|
matches!(input(&canvas, &mut state, mouse::Event::ButtonPressed(mouse::Button::Right), pin),
|
|
Some(Event::ContextMenu { context, object, placement: None, anchor, bend: Some(0), route_segment: None, .. })
|
|
if context == canvas.context && object == relation && anchor == pin)
|
|
);
|
|
let (index, midpoint) = bend_segment(&scene, relation);
|
|
let at = screen(&canvas, midpoint);
|
|
motion(&canvas, &mut state, at, at);
|
|
assert!(
|
|
matches!(input(&canvas, &mut state, mouse::Event::ButtonPressed(mouse::Button::Right), at),
|
|
Some(Event::ContextMenu { context, object, placement: None, anchor, bend: None, route_segment: Some((segment, world)), .. })
|
|
if context == canvas.context && object == relation && anchor == at && segment == index && point(world).distance(midpoint) < 0.001)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_internal_rows_and_folded_proxies_cannot_acquire_pins() {
|
|
for internal in [false, true] {
|
|
let mut f = fixture();
|
|
let relation = if internal {
|
|
let id = f
|
|
.project
|
|
.relate(RelationKind::StateTransition, "Internal check", f.a, f.a);
|
|
if let RelationBody::StateTransition {
|
|
transition_kind, ..
|
|
} = &mut f.project.relations.get_mut(&id).unwrap().body
|
|
{
|
|
*transition_kind = TransitionKind::Internal;
|
|
}
|
|
id
|
|
} else {
|
|
let id = f.project.relate(
|
|
RelationKind::StateTransition,
|
|
"Hidden child to standby",
|
|
f.leaf,
|
|
f.b,
|
|
);
|
|
let parent = placement(&f, f.a);
|
|
f.project
|
|
.diagrams
|
|
.get_mut(&f.diagram)
|
|
.unwrap()
|
|
.nodes
|
|
.get_mut(&parent)
|
|
.unwrap()
|
|
.collapsed = true;
|
|
id
|
|
};
|
|
let mut canvas = canvas(&f, Tool::Select, 1.);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let scene = canvas.scene(&state).unwrap();
|
|
let world = if internal {
|
|
assert!(!scene.routed.edges.contains_key(&relation));
|
|
let node = scene.view.nodes.iter().find(|n| n.element == f.a).unwrap();
|
|
internal_rows(&f.project, node)
|
|
.into_iter()
|
|
.find(|(r, _)| r.id == relation)
|
|
.unwrap()
|
|
.1
|
|
.center()
|
|
} else {
|
|
assert!(scene.routed.edges[&relation].custom_suspended);
|
|
bend_segment(&scene, relation).1
|
|
};
|
|
let start = screen(&canvas, world);
|
|
motion(&canvas, &mut state, start, start);
|
|
assert!(matches!(
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonPressed(mouse::Button::Right),
|
|
start
|
|
),
|
|
Some(Event::ContextMenu {
|
|
bend: None,
|
|
route_segment: None,
|
|
..
|
|
})
|
|
));
|
|
bend_press(&canvas, &mut state, start);
|
|
let away = start + Vector::new(30., 20.);
|
|
motion(&canvas, &mut state, away, away);
|
|
assert!(state.bend_preview.is_none());
|
|
assert!(!matches!(
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
away
|
|
),
|
|
Some(Event::SetBends { .. })
|
|
));
|
|
assert!(f.project.diagrams[&f.diagram].routes.is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_right_click_discards_provisional_indexes_before_building_menu() {
|
|
for click_pin in [true, false] {
|
|
let (mut f, relation) = bend_fixture(true);
|
|
let route = &mut f.project.diagrams.get_mut(&f.diagram).unwrap().routes[0];
|
|
route.waypoints = vec![Position { x: 650., y: 340. }, Position { x: 650., y: 390. }];
|
|
validate_candidate(&f.project).unwrap();
|
|
let original = f.project.diagrams[&f.diagram].routes[0].clone();
|
|
let mut canvas = canvas(&f, Tool::Select, 1.);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let baseline = canvas.scene(&state).unwrap();
|
|
let edge = &baseline.routed.edges[&relation];
|
|
let candidates: Vec<_> = edge.points.windows(2).enumerate()
|
|
.filter(|(i, _)| *i > 0 && *i + 2 < edge.points.len())
|
|
.map(|(i, p)| (i, Point::new((p[0].x + p[1].x) / 2., (p[0].y + p[1].y) / 2.)))
|
|
.filter(|(_, p)| edge.pins.iter().all(|pin| p.distance(point(*pin)) > 20.))
|
|
.filter(|(_, p)| matches!(canvas.hit(&baseline, *p), Some(Hit::Transition(id)) if id == relation))
|
|
.filter(|(_, p)| !Rectangle::new(point(edge.label.bounds.position), Size::new(edge.label.bounds.size.width, edge.label.bounds.size.height)).contains(*p))
|
|
.collect();
|
|
let (_, insertion_at) = candidates
|
|
.iter()
|
|
.copied()
|
|
.find(|(i, p)| routing::pin_insertion_index(edge, *i, position(*p)) == 0)
|
|
.expect("exposed segment before both authored pins");
|
|
let menu_segment = candidates
|
|
.iter()
|
|
.copied()
|
|
.find(|(_, p)| p.distance(insertion_at) > 30.)
|
|
.expect("a distinct committed segment for the menu control");
|
|
let start = screen(&canvas, insertion_at);
|
|
bend_press(&canvas, &mut state, start);
|
|
let away = start + Vector::new(45., 30.);
|
|
motion(&canvas, &mut state, away, away);
|
|
let preview = state.bend_preview.as_ref().unwrap();
|
|
assert_eq!(preview.waypoints.len(), 3);
|
|
assert_eq!(
|
|
&preview.waypoints[1..],
|
|
original.waypoints.as_slice(),
|
|
"provisional insertion shifts both stored indices"
|
|
);
|
|
|
|
// With the left gesture still active, moving to the last stored pin also
|
|
// moves provisional pin0 there. The preview's first hit is therefore
|
|
// pin0, but a menu built after cancellation must identify stored pin1.
|
|
let menu_world = if click_pin {
|
|
point(original.waypoints[1])
|
|
} else {
|
|
menu_segment.1
|
|
};
|
|
let menu_window = screen(&canvas, menu_world);
|
|
motion(&canvas, &mut state, menu_window, menu_window);
|
|
assert!(state.bend_preview.is_some());
|
|
let menu = input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonPressed(mouse::Button::Right),
|
|
menu_window,
|
|
)
|
|
.unwrap();
|
|
let Event::ContextMenu {
|
|
context,
|
|
object,
|
|
bend,
|
|
route_segment,
|
|
..
|
|
} = menu
|
|
else {
|
|
panic!("expected context menu after cancelling drag")
|
|
};
|
|
assert_eq!(context, canvas.context);
|
|
assert_eq!(object, relation);
|
|
assert!(state.bend_preview.is_none());
|
|
assert_eq!(
|
|
canvas.scene(&state).unwrap().routed.edges[&relation].points,
|
|
edge.points
|
|
);
|
|
if click_pin {
|
|
assert_eq!(
|
|
bend,
|
|
Some(1),
|
|
"menu index must address the stored route, not the provisional route"
|
|
);
|
|
assert!(route_segment.is_none());
|
|
let remove = Event::RemoveBend {
|
|
context,
|
|
relation,
|
|
index: bend.unwrap(),
|
|
};
|
|
let plan = state_edit::plan(&f.project, context, &remove).unwrap();
|
|
let after = prepare_command(&f.project, &plan.command).unwrap().project;
|
|
let mut expected = f.project.clone();
|
|
expected.diagrams.get_mut(&f.diagram).unwrap().routes[0]
|
|
.waypoints
|
|
.remove(1);
|
|
assert_eq!(
|
|
after, expected,
|
|
"remove menu action must delete exactly the addressed original pin"
|
|
);
|
|
} else {
|
|
assert!(
|
|
bend.is_none(),
|
|
"provisional pin under cursor must not leak into menu"
|
|
);
|
|
let (segment, at) = route_segment.expect("committed route segment remains actionable");
|
|
assert_eq!(segment, menu_segment.0);
|
|
assert!(point(at).distance(menu_world) < 0.001);
|
|
}
|
|
assert!(
|
|
input(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
menu_window
|
|
)
|
|
.is_none()
|
|
);
|
|
assert_eq!(f.project.diagrams[&f.diagram].routes[0], original);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn state_bends_draw_before_update_excludes_preview_after_camera_or_bounds_change() {
|
|
for change in 0..4 {
|
|
let (f, relation) = bend_fixture(true);
|
|
let mut canvas = canvas(&f, Tool::Select, 1.);
|
|
canvas.selected = relation;
|
|
let mut state = State::default();
|
|
let baseline = canvas.scene_with_bounds(&state, Some(bounds())).unwrap();
|
|
let start = screen(&canvas, point(baseline.routed.edges[&relation].pins[0]));
|
|
let release = start + Vector::new(45., 30.);
|
|
bend_press(&canvas, &mut state, start);
|
|
motion(&canvas, &mut state, release, release);
|
|
let displayed_preview = canvas.scene_with_bounds(&state, Some(bounds())).unwrap();
|
|
assert_ne!(
|
|
displayed_preview.routed.edges[&relation].pins,
|
|
baseline.routed.edges[&relation].pins
|
|
);
|
|
let mut changed_bounds = bounds();
|
|
match change {
|
|
0 => canvas.zoom = 2.,
|
|
1 => canvas.pan.y += 25.,
|
|
2 => changed_bounds.x += 35.,
|
|
_ => changed_bounds.width -= 90.,
|
|
}
|
|
// No update event between camera/layout change and this draw-time read.
|
|
let shown = canvas
|
|
.scene_with_bounds(&state, Some(changed_bounds))
|
|
.unwrap();
|
|
assert_eq!(
|
|
shown.routed.edges[&relation].pins, baseline.routed.edges[&relation].pins,
|
|
"stale draw-time pins for case {change}"
|
|
);
|
|
assert_eq!(
|
|
shown.routed.edges[&relation].points, baseline.routed.edges[&relation].points,
|
|
"stale cached preview for case {change}"
|
|
);
|
|
let event = input_at_bounds(
|
|
&canvas,
|
|
&mut state,
|
|
mouse::Event::ButtonReleased(mouse::Button::Left),
|
|
release,
|
|
changed_bounds,
|
|
);
|
|
assert!(!matches!(event, Some(Event::SetBends { .. })));
|
|
assert!(state.bend_preview.is_none());
|
|
}
|
|
}
|