Fix-up #11243: Free Transform a Copy of a linked layer makes no copy

Measured in the reference application: Ctrl+Alt+T on a linked layer without
a selection makes no copy; the box frames and moves the whole link group, and
History shows one Free Transform. An unlinked layer still gets its copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David MannandClaude Opus 5.5 committed 2026-11-04 16:33:00 +01:00
1 parent ff7d36fe4b
commit a8b69338da
2 files changed
+31

No files matched your search

@@ -255,6 +255,16 @@ pub const WARP_ONE_LAYER: &str = "Warp works on one layer at a time: select a si
/// its selected pixels, as Layer via Copy) and transforms the copy (#352).
pub fn begin_copy(app: &mut PhotocraftApp, ctx: &egui::Context) -> Result<(), String> {
let selection = app.session.active().is_some_and(|d| d.doc.selection.is_some());
// A linked layer without a selection: the reference application makes no copy and frames the
// whole link group, as Free Transform does (measured, #11217). Unlinked layers get the copy.
let linked = !selection
&& app.session.active().is_some_and(|st| {
let targets = photocraft_engine::transform_cmds::selection_targets(st);
photocraft_engine::transform_cmds::box_targets(&st.doc, &targets) != targets
});
if linked {
return begin(app, ctx);
}
let params = if selection { json!({}) } else { json!({"inPlace": true}) };
app.run(if selection { "layer.new.layerViaCopy" } else { "layer.duplicate" }, params)?;
let made = app.session.active().and_then(|st| st.active_layer.map(|layer| (st.doc.id, layer)));
@@ -2090,6 +2090,27 @@ fn linked_layers_app() -> (PhotocraftApp, LayerId, LayerId, LayerId) {
(app, a, b, c)
}
/// #11217: ⌥⌘T on a linked layer without a selection makes no copy in the reference application
/// (measured): it frames and moves the whole link group, as Free Transform does. An unlinked
/// layer still gets its copy.
#[test]
fn free_transform_a_copy_of_a_linked_layer_boxes_the_link_group_without_a_copy() {
let ctx = egui::Context::default();
let (mut app, a, b, c) = linked_layers_app();
let layers = app.session.active().unwrap().doc.layers.len();
crate::menus::invoke(&mut app, &ctx, "edit.freeTransformCopy", json!({})).unwrap();
assert_eq!(app.session.active().unwrap().doc.layers.len(), layers, "no copy of a linked layer");
let t = app.ui.transform.as_ref().unwrap();
assert_eq!(t.made, None, "nothing made to take back");
assert_eq!(t.layers, [a.0, b.0], "the box frames the link group");
cancel(&mut app);
app.session.execute("layer.select", json!({"layer": c.0})).unwrap();
crate::menus::invoke(&mut app, &ctx, "edit.freeTransformCopy", json!({})).unwrap();
assert_eq!(app.session.active().unwrap().doc.layers.len(), layers + 1, "an unlinked layer is copied");
assert_eq!(app.ui.transform.as_ref().unwrap().made, Some(MadeLayer::Copy));
cancel(&mut app);
}
/// #11217: the box over one selected linked layer frames, previews and commits its whole link
/// group, as in Photoshop. It used to frame A alone; B only moved at the commit.
#[test]