mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Appearance: Reduce to Basic Appearance keeps the selected fill or stroke row
Reduce to Basic Appearance kept the topmost visible fill and stroke even when another fill or stroke row was selected in the Appearance panel. It now keeps the selected row in place of the topmost one of its kind, and in each other selected object the row equal to it. With ids, or with no selected row, it keeps the topmost ones as before.
This commit is contained in:
@@ -71,7 +71,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Reduce to Basic Appearance",
|
||||
["Window", "Appearance"],
|
||||
None,
|
||||
"{ids?|id?, target?} keep only the topmost visible fill and stroke, without their own opacity, blend mode or effects (a group keeps no rows it lacked), and drop the object's effects",
|
||||
"{ids?|id?, target?} keep only one visible fill and stroke (without ids: the Appearance panel's active row in place of the topmost one of its kind; else the topmost), without their own opacity, blend mode or effects (a group keeps no rows it lacked), and drop the object's effects",
|
||||
has_doc,
|
||||
reduce_basic
|
||||
),
|
||||
@@ -614,11 +614,26 @@ fn reduce_basic(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ids = require_targets(s, p, "appearance.reduceToBasic")?;
|
||||
// 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)
|
||||
}
|
||||
_ => vec![Vec::new(); ids.len()],
|
||||
};
|
||||
super::overprint::edit_counted(s, "Reduce to Basic Appearance", |d| {
|
||||
for id in &ids {
|
||||
for (id, at) in ids.iter().zip(&active) {
|
||||
if let Some(n) = d.node_mut(*id) {
|
||||
// The topmost visible fill and stroke stay, without their own transparency or effects.
|
||||
let top = |fill: bool| n.appearance.items.iter().rev().find(|i| i.visible() && i.is_fill() == fill).cloned();
|
||||
// The active row stays when it is a visible fill or stroke; otherwise the topmost
|
||||
// visible one of each kind does. Both lose their own transparency and effects.
|
||||
let picked = at.first().and_then(|i| n.appearance.items.get(*i)).filter(|i| i.visible()).cloned();
|
||||
let top = |fill: bool| match &picked {
|
||||
Some(i) if i.is_fill() == fill => Some(i.clone()),
|
||||
_ => n.appearance.items.iter().rev().find(|i| i.visible() && i.is_fill() == fill).cloned(),
|
||||
};
|
||||
let (fill, stroke) = (top(true), top(false));
|
||||
// Other objects always keep a fill row (None when there was none); a group only
|
||||
// keeps the rows it had.
|
||||
|
||||
@@ -400,6 +400,50 @@ fn clear_and_reduce_to_basic() {
|
||||
assert_eq!(node(&s, id).opacity, 0.4);
|
||||
}
|
||||
|
||||
/// Reduce to Basic Appearance keeps the Appearance panel's active fill or stroke row instead of the
|
||||
/// topmost one of its kind, in each selected object with the same stack. With `ids` it keeps the
|
||||
/// topmost ones.
|
||||
#[test]
|
||||
fn reduce_to_basic_keeps_the_active_row() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 50.0);
|
||||
let b = rect(&mut s, 200.0);
|
||||
let c = rect(&mut s, 300.0);
|
||||
let stack = |s: &mut Session| {
|
||||
run(s, "appearance.addFill", json!({}));
|
||||
run(s, "appearance.addStroke", json!({})); // [Fill, Stroke, Fill, Stroke]
|
||||
run(s, "appearance.setItem", json!({"index": 0, "color": "#00ff00"}));
|
||||
run(s, "appearance.setItem", json!({"index": 2, "color": "#ff0000"}));
|
||||
run(s, "appearance.setItem", json!({"index": 1, "weight": 7}));
|
||||
};
|
||||
run(&mut s, "select.set", json!({"ids": [a.0, b.0]}));
|
||||
stack(&mut s);
|
||||
// The lower, green fill row is active: both objects keep it and their top stroke.
|
||||
run(&mut s, "appearance.setActiveItem", json!({"index": 0}));
|
||||
run(&mut s, "appearance.reduceToBasic", json!({}));
|
||||
for id in [a, b] {
|
||||
let n = node(&s, id);
|
||||
assert!(n.appearance.is_basic(), "{:?}", n.appearance);
|
||||
assert_eq!(fill_paint(&n, 0).color().unwrap().to_hex(), "#00ff00");
|
||||
assert_eq!(stroke(&n, 1).width, 1.0);
|
||||
}
|
||||
// The lower stroke row is active: it stays, with the top fill.
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
run(&mut s, "appearance.setActiveItem", json!({"index": 1}));
|
||||
run(&mut s, "appearance.reduceToBasic", json!({}));
|
||||
let n = node(&s, a);
|
||||
assert_eq!(fill_paint(&n, 0).color().unwrap().to_hex(), "#ff0000");
|
||||
assert_eq!(stroke(&n, 1).width, 7.0);
|
||||
// Given ids, the topmost fill and stroke stay.
|
||||
run(&mut s, "select.set", json!({"ids": [c.0]}));
|
||||
stack(&mut s);
|
||||
run(&mut s, "appearance.setActiveItem", json!({"index": 0}));
|
||||
run(&mut s, "appearance.reduceToBasic", json!({"ids": [c.0]}));
|
||||
let n = node(&s, c);
|
||||
assert_eq!(fill_paint(&n, 0).color().unwrap().to_hex(), "#ff0000");
|
||||
assert_eq!(stroke(&n, 1).width, 1.0);
|
||||
}
|
||||
|
||||
/// 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();
|
||||
|
||||
Reference in New Issue
Block a user