diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 8d4bde60c..585e4459e 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -105,10 +105,11 @@ pub(crate) fn reid(layer: &mut Layer, next: &mut u64) { } fn duplicate(s: &mut Session, p: &Value) -> Result { + let editing = s.state.text_edit.is_some(); // Effects selected → duplicate them (Edit ▸ Duplicate in Effect Controls). An open text edit // comes first, as it does for Clear: opening the editor keeps the property selection, but // while typing, Duplicate means the edited layer, not an effect or item selected before. - if p.get("layers").is_none() && s.state.text_edit.is_none() { + if p.get("layers").is_none() && !editing { let fx = super::effect::selected_effects(s); if !fx.is_empty() { let mut out = vec![]; @@ -151,6 +152,11 @@ fn duplicate(s: &mut Session, p: &Value) -> Result { relink_copies(comp, &map); proj.next_id = next; st.selected_layers = created.clone(); + // The property selection the edit kept was passed over above and belongs to the + // originals: left in place, the next Duplicate or Clear would act on it, not the copies. + if editing { + st.selected_props.clear(); + } Ok(created) })?; Ok(json!(new.iter().map(|l| l.0).collect::>())) diff --git a/crates/engine/src/tests/text_edit.rs b/crates/engine/src/tests/text_edit.rs index 64ee2f0e3..1713cd459 100644 --- a/crates/engine/src/tests/text_edit.rs +++ b/crates/engine/src/tests/text_edit.rs @@ -1265,11 +1265,16 @@ fn duplicate_during_an_edit_copies_the_layer_not_a_selected_property_group() { assert_eq!(items(&s).len(), n, "{what}: the selected {what} is not duplicated"); assert_eq!(layers(&s), before + 1, "{what}: the edited layer is"); assert_eq!(s.state.selected_layers, vec![LayerId(r[0].as_u64().unwrap())], "{what}: and its copy is selected"); + // The selection the edit passed over goes with it: the next Duplicate copies the selected + // copy rather than reaching back to the original's {what}. + s.execute("edit.duplicate", json!({})).unwrap(); + assert_eq!(items(&s).len(), n, "{what}: a second Duplicate leaves the original's {what} alone"); + assert_eq!(layers(&s), before + 2, "{what}: and copies the copy"); // With no edit open, the selected item keeps its precedence over the layers. s.execute("prop.select", json!({"layer": owner, "prop": item})).unwrap(); s.execute("edit.duplicate", json!({})).unwrap(); assert_eq!(items(&s).len(), n + 1, "{what}: outside an edit Duplicate copies the selected {what}"); - assert_eq!(layers(&s), before + 1, "{what}: and no layer"); + assert_eq!(layers(&s), before + 2, "{what}: and no layer"); } }