#2817: honor combined path and shape group selections

This commit is contained in:
arnav-hiwarkar committed 2026-11-02 22:16:19 +05:30
1 parent c5bf611909
commit 2b1b688557
2 files changed
+35 -12

No files matched your search

+9 -11
View File
@@ -38,19 +38,18 @@ fn targets(s: &Session) -> Result<Vec<Target>> {
props: s.state.selected_props.iter().filter(|(lid, _)| s.state.selected_layers.contains(lid)).copied().collect(),
time: s.time(),
};
let whole_layers = selection.vertices.is_empty() && selection.props.is_empty();
let mut targets = Vec::new();
for layer in comp.layers.iter() {
let selected_layer = if !selection.vertices.is_empty() {
selection.vertices.iter().any(|v| v.layer == layer.id)
} else if !selection.props.is_empty() {
selection.props.iter().any(|(lid, _)| *lid == layer.id)
} else {
let selected_layer = if whole_layers {
s.state.selected_layers.contains(&layer.id)
} else {
selection.vertices.iter().any(|v| v.layer == layer.id) || selection.props.iter().any(|(lid, _)| *lid == layer.id)
};
if !selected_layer || layer.switches.locked {
continue;
}
collect(&selection, layer, &layer.props, &mut Vec::new(), selection.props.is_empty(), &mut targets)?;
collect(&selection, layer, &layer.props, &mut Vec::new(), whole_layers, &mut targets)?;
}
if targets.is_empty() {
return Err(bad("path.toggleClosed", "select an unlocked mask or Bezier shape path"));
@@ -70,11 +69,10 @@ fn collect(selection: &Selection, layer: &Layer, group: &PropGroup, route: &mut
&& let Some(prop) = group.get("path")
&& let KV::Path(path) = prop.value_at(layer.layer_time(selection.time))
{
let selected = if selection.vertices.is_empty() {
selected || selection.props.contains(&(layer.id, prop.uid))
} else {
selection.vertices.iter().any(|v| v.layer == layer.id && v.mask == group.uid && v.index < path.vertices.len())
};
// Property selection also selects its points; both describe the selected paths.
let selected = selected
|| selection.props.contains(&(layer.id, prop.uid))
|| selection.vertices.iter().any(|v| v.layer == layer.id && v.mask == group.uid && v.index < path.vertices.len());
if selected {
targets.push(Target { layer: layer.id, route: route.clone(), closed: path.closed });
}
+26 -1
View File
@@ -302,7 +302,7 @@ fn toggle_closed_respects_path_and_vertex_selection() {
s.state.selected_props = vec![(LayerId(lid), b)];
s.state.selected_vertices = vec![crate::VertexRef { layer: LayerId(lid), mask: a, index: 1 }];
s.execute("path.toggleClosed", json!({})).unwrap();
assert!(shape_path(&s, lid, a).closed, "vertices take precedence over property selection");
assert!(shape_path(&s, lid, a).closed, "selected points and properties both describe target paths");
assert!(shape_path(&s, lid, b).closed);
s.state.selected_vertices.clear();
s.state.selected_props = vec![(LayerId(lid), a), (LayerId(lid), leaf), (LayerId(lid), b)];
@@ -323,6 +323,31 @@ fn toggle_closed_respects_path_and_vertex_selection() {
assert!(!shape_path(&s, lid, b).closed, "layer selection addresses all editable paths");
}
#[test]
fn toggle_closed_combines_property_and_shape_group_selections() {
let mut s = comp();
let a = s.execute("shape.newPath", json!({"vertices": [[0, 0], [100, 0], [100, 100]], "closed": true})).unwrap();
let (lid, a_path) = (a["layer"].as_u64().unwrap(), a["path"].as_u64().unwrap());
let b = s.execute("shape.newPath", json!({"layer": lid, "vertices": [[10, 10], [90, 10], [90, 90]], "closed": true})).unwrap();
let (b_path, b_group) = (b["path"].as_u64().unwrap(), b["group"].as_u64().unwrap());
let c_path =
s.execute("shape.newPath", json!({"layer": lid, "vertices": [[20, 20], [80, 20], [80, 80]], "closed": true})).unwrap()["path"].as_u64().unwrap();
s.execute("prop.select", json!({"layer": lid, "prop": a_path})).unwrap();
s.execute("prop.select", json!({"layer": lid, "prop": b_group, "add": true})).unwrap();
assert!(s.state.selected_vertices.iter().all(|v| v.mask == a_path), "only the path property implies selected vertices");
assert!(!s.state.selected_vertices.is_empty());
assert!(s.state.selected_props.contains(&(LayerId(lid), b_group)));
let result = s.execute("path.toggleClosed", json!({})).unwrap();
assert_eq!(result["paths"], json!(2));
assert!(!shape_path(&s, lid, a_path).closed);
assert!(!shape_path(&s, lid, b_path).closed, "the added shape group is included alongside implied vertices");
assert!(shape_path(&s, lid, c_path).closed, "unselected sibling stays unchanged");
s.execute("edit.undo", json!({})).unwrap();
assert!(shape_path(&s, lid, a_path).closed);
assert!(shape_path(&s, lid, b_path).closed, "both selected paths undo together");
assert!(shape_path(&s, lid, c_path).closed);
}
#[test]
fn toggle_closed_ignores_selections_retained_on_other_layers() {
let mut s = comp();