Fix #2678: Ungroup no longer multiplies the group's opacity into its members

Ungroup multiplied each member's opacity by the group's, so a member of a
50% group stayed at 50% after the group was gone, and members that
overlapped showed through each other. Each member now keeps its own
opacity, and the group's opacity is removed with the group.
This commit is contained in:
dotCooCoo
2026-11-01 01:22:08 +07:00
committed by bflatastic
parent d2d4315524
commit db6c5ea9df
2 changed files with 23 additions and 1 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ pub(super) fn ungroup(s: &mut Session, _: &Value) -> Result<Value> {
{
*clipping = false;
}
c.opacity *= n.opacity;
// Each member keeps its own opacity; the group's opacity goes with the group.
new_sel.push(c.id);
out.push(c);
}
+22
View File
@@ -69,6 +69,28 @@ fn a_group_fill_paints_both_members_and_the_contents_slot_moves() {
assert!(s.execute("appearance.moveItem", &json!({"from": "contents", "to": 1})).is_err());
}
#[test]
fn ungroup_leaves_the_groups_opacity_with_the_group() {
let mut s = session();
let red = id_of(&run(&mut s, "shape.rectangle", json!({"x": 20, "y": 20, "width": 60, "height": 40})));
run(&mut s, "paint.setFill", json!({"color": "#ff0000"}));
let blue = id_of(&run(&mut s, "shape.rectangle", json!({"x": 100, "y": 20, "width": 60, "height": 40})));
run(&mut s, "paint.setFill", json!({"color": "#0000ff"}));
run(&mut s, "object.setProps", json!({"ids": [blue.0], "opacity": 40}));
run(&mut s, "select.set", json!({"ids": [red.0, blue.0]}));
let g = id_of(&run(&mut s, "object.group", json!({})));
run(&mut s, "object.setProps", json!({"ids": [g.0], "opacity": 50}));
run(&mut s, "object.ungroup", json!({}));
// Each member keeps its own opacity: the red square is opaque again and the blue one stays at 40%.
assert!((node(&s, red).opacity - 1.0).abs() < 1e-6, "{}", node(&s, red).opacity);
assert!((node(&s, blue).opacity - 0.4).abs() < 1e-6, "{}", node(&s, blue).opacity);
assert_eq!(pixel(&s, 40, 40), RED);
// One undo step brings the group back at 50%.
run(&mut s, "edit.undo", json!({}));
assert!((node(&s, g).opacity - 0.5).abs() < 1e-6);
assert!((node(&s, red).opacity - 1.0).abs() < 1e-6);
}
#[test]
fn appearance_commands_target_the_object_or_its_contents() {
let mut s = session();