Appearance: Reduce to Basic Appearance finds the active row in the first selected object

The targets are in paint order, so the first selected object is not always the first target. The active row is now matched against the first selected object wherever it is among them.
This commit is contained in:
dotCooCoo
2026-10-31 19:43:15 +07:00
committed by bflatastic
parent d9fefde061
commit 45b266c805
2 changed files with 29 additions and 6 deletions
+10 -6
View File
@@ -615,12 +615,16 @@ fn reduce_basic(s: &mut Session, p: &Value) -> Result<Value> {
// When every object comes out as it was, the document and its undo history stay as they are.
let before = s.doc()?.doc.clone();
// The Appearance panel's active row (a row of the first selected object's stack, and in each
// other target the row equal to it there), when the command acts on that selection.
let active = match s.appearance_item() {
Some(index)
if p.get("ids").is_none() && p.get("id").is_none() && !targets_contents(p)? && s.doc()?.selection.subjects().first() == ids.first() =>
{
items_per_target(&before, &ids, &[index], true)
// other target the row equal to it there), when the command acts on that selection. The targets
// are in paint order, so the first selected object can be any of them.
let first = s.doc()?.selection.subjects().first().and_then(|f| ids.iter().position(|id| id == f));
let active = match (s.appearance_item(), first) {
(Some(index), Some(k)) if p.get("ids").is_none() && p.get("id").is_none() && !targets_contents(p)? => {
let mut from = ids.clone();
from.swap(0, k);
let mut rows = items_per_target(&before, &from, &[index], true);
rows.swap(0, k);
rows
}
_ => vec![Vec::new(); ids.len()],
};
+19
View File
@@ -444,6 +444,25 @@ fn reduce_to_basic_keeps_the_active_row() {
assert_eq!(stroke(&n, 1).width, 1.0);
}
/// The active row is a row of the first selected object even when that object paints above the
/// others; another object without an equal row keeps its topmost fill.
#[test]
fn reduce_to_basic_keeps_the_active_row_of_the_first_selected_object() {
let mut s = session();
let a = rect(&mut s, 50.0);
run(&mut s, "appearance.addFill", json!({}));
run(&mut s, "appearance.setItem", json!({"index": 2, "color": "#0000ff"})); // [Fill, Stroke, Fill blue]
let b = rect(&mut s, 200.0);
run(&mut s, "appearance.addFill", json!({}));
run(&mut s, "appearance.setItem", json!({"index": 0, "color": "#00ff00"}));
run(&mut s, "appearance.setItem", json!({"index": 2, "color": "#ff0000"})); // [Fill green, Stroke, Fill red]
run(&mut s, "select.set", json!({"ids": [b.0, a.0]}));
run(&mut s, "appearance.setActiveItem", json!({"index": 0}));
run(&mut s, "appearance.reduceToBasic", json!({}));
assert_eq!(fill_paint(&node(&s, b), 0).color().unwrap().to_hex(), "#00ff00");
assert_eq!(fill_paint(&node(&s, a), 0).color().unwrap().to_hex(), "#0000ff");
}
/// How many steps there are to undo and to redo, and whether the document has unsaved changes.
fn history(s: &Session) -> (usize, usize, bool) {
let st = s.doc().unwrap();