Merge pull request #1830 from switch87/fix/1735-mask-global-swatch
Global swatch edits reach opacity-mask art (#1735)
This commit is contained in:
commit
1d62d75150
|
|
@ -109,10 +109,11 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Visit every colour that can link to a swatch — solid paints and gradient stops of fills,
|
/// Visit every colour that can link to a swatch — solid paints and gradient stops of fills,
|
||||||
/// strokes and text runs — in the art, symbol definitions, pattern tiles, graphic styles and the
|
/// strokes and text runs — in the art (opacity masks included), symbol definitions, pattern
|
||||||
/// swatches themselves (tint swatches, gradient swatches' stops). `f(colour, link, tint)` may
|
/// tiles, graphic styles and the swatches themselves (tint swatches, gradient swatches' stops).
|
||||||
/// change them and returns true when it did ([`Paint::map_links`]). Only nodes holding a changed
|
/// `f(colour, link, tint)` may change them and returns true when it did
|
||||||
/// paint (and the paths to them) are copied. Returns the number of changed paints.
|
/// ([`Paint::map_links`]). Only nodes holding a changed paint (and the paths to them) are
|
||||||
|
/// copied. Returns the number of changed paints.
|
||||||
pub fn map_solid_paints(&mut self, f: &mut LinkFn) -> usize {
|
pub fn map_solid_paints(&mut self, f: &mut LinkFn) -> usize {
|
||||||
let mut n = map_trees(&mut self.layers, f);
|
let mut n = map_trees(&mut self.layers, f);
|
||||||
for s in &mut self.symbols {
|
for s in &mut self.symbols {
|
||||||
|
|
@ -152,11 +153,12 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Document {
|
impl Document {
|
||||||
/// Visit every paint held by the art, symbol definitions, pattern tiles and graphic styles
|
/// Visit every paint held by the art (opacity masks included), symbol definitions, pattern
|
||||||
/// (fills, strokes and text runs), and the colour of every gradient-mesh point as a solid paint.
|
/// tiles and graphic styles (fills, strokes and text runs), and the colour of every
|
||||||
|
/// gradient-mesh point as a solid paint.
|
||||||
pub fn visit_paints(&self, f: &mut dyn FnMut(&Paint)) {
|
pub fn visit_paints(&self, f: &mut dyn FnMut(&Paint)) {
|
||||||
let mut tree = |n: &Node| {
|
let mut tree = |n: &Node| {
|
||||||
n.walk(&mut |m| {
|
walk_with_masks(n, &mut |m| {
|
||||||
node_paints(m).for_each(&mut *f);
|
node_paints(m).for_each(&mut *f);
|
||||||
if let NodeKind::Mesh(mesh) = &m.kind {
|
if let NodeKind::Mesh(mesh) = &m.kind {
|
||||||
mesh.points.iter().for_each(|p| f(&Paint::solid(p.color)));
|
mesh.points.iter().for_each(|p| f(&Paint::solid(p.color)));
|
||||||
|
|
@ -280,9 +282,31 @@ fn map_node(n: &Node, f: &mut LinkFn) -> Option<(Node, usize)> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Opacity mask art lives outside the tree, but its colours are the object's too (#1735).
|
||||||
|
if let Some(mask) = n.extras().mask.as_deref()
|
||||||
|
&& let Some((new, k)) = map_node(&mask.art, f)
|
||||||
|
{
|
||||||
|
let m = out.get_or_insert_with(|| n.clone());
|
||||||
|
if let Some(slot) = m.extras_if_any().and_then(|e| e.mask.as_deref_mut()) {
|
||||||
|
slot.art = Arc::new(new);
|
||||||
|
count += k;
|
||||||
|
}
|
||||||
|
}
|
||||||
out.map(|m| (m, count))
|
out.map(|m| (m, count))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Visit `n` and its subtree, the art of opacity masks included (mask art after the masked
|
||||||
|
/// object, before its children).
|
||||||
|
fn walk_with_masks<'a>(n: &'a Node, f: &mut dyn FnMut(&'a Node)) {
|
||||||
|
f(n);
|
||||||
|
if let Some(mask) = n.extras().mask.as_deref() {
|
||||||
|
walk_with_masks(&mask.art, f);
|
||||||
|
}
|
||||||
|
for c in n.children().into_iter().flatten() {
|
||||||
|
walk_with_masks(c, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn map_tree(n: &mut Arc<Node>, f: &mut LinkFn) -> usize {
|
fn map_tree(n: &mut Arc<Node>, f: &mut LinkFn) -> usize {
|
||||||
match map_node(n, f) {
|
match map_node(n, f) {
|
||||||
Some((new, count)) => {
|
Some((new, count)) => {
|
||||||
|
|
|
||||||
|
|
@ -353,3 +353,33 @@ fn a_group_from_a_mesh_holds_its_point_colours() {
|
||||||
let names = r["swatches"].as_array().unwrap();
|
let names = r["swatches"].as_array().unwrap();
|
||||||
assert!(names.contains(&json!("R=255 G=0 B=0")) && names.contains(&json!("R=255 G=255 B=255")), "{r}");
|
assert!(names.contains(&json!("R=255 G=0 B=0")) && names.contains(&json!("R=255 G=255 B=255")), "{r}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #1735: a global swatch edit reaches opacity-mask art: the mask's colour (and with it what the
|
||||||
|
/// mask shows) follows the swatch, and a swatch used only in a mask isn't "unused".
|
||||||
|
#[test]
|
||||||
|
fn global_swatch_edits_reach_opacity_mask_art() {
|
||||||
|
let mut s = session();
|
||||||
|
run(&mut s, "swatch.new", json!({"name": "MaskTone", "color": "#ffffff", "global": true}));
|
||||||
|
let obj = rect(&mut s);
|
||||||
|
run(&mut s, "paint.setFill", json!({"color": "#ff0000", "ids": [obj.0]}));
|
||||||
|
let m = rect(&mut s);
|
||||||
|
run(&mut s, "paint.setFill", json!({"swatch": "MaskTone", "ids": [m.0]}));
|
||||||
|
run(&mut s, "select.set", json!({"ids": [obj.0, m.0]}));
|
||||||
|
run(&mut s, "transparency.makeOpacityMask", json!({"clip": true, "invert": false}));
|
||||||
|
let mask_fill = |s: &Session| {
|
||||||
|
let n = doc(s).node(obj).unwrap();
|
||||||
|
n.extras().mask.as_deref().unwrap().art.appearance.fill_paint()
|
||||||
|
};
|
||||||
|
assert_eq!(mask_fill(&s).color(), Some(Color::rgb(1.0, 1.0, 1.0)));
|
||||||
|
assert!(!run(&mut s, "swatch.unused", json!({}))["names"].as_array().unwrap().iter().any(|n| n == "MaskTone"), "used by the mask");
|
||||||
|
|
||||||
|
let r = run(&mut s, "swatch.edit", json!({"name": "MaskTone", "color": "#000000"}));
|
||||||
|
assert_eq!(r["relinked"], 1, "{r}");
|
||||||
|
let fill = mask_fill(&s);
|
||||||
|
assert_eq!(fill.color(), Some(Color::rgb(0.0, 0.0, 0.0)));
|
||||||
|
assert!(matches!(&fill, Paint::Solid { swatch: Some(n), .. } if n == "MaskTone"), "{fill:?}");
|
||||||
|
|
||||||
|
// Undo brings the mask's colour back with the swatch.
|
||||||
|
run(&mut s, "edit.undo", json!({}));
|
||||||
|
assert_eq!(mask_fill(&s).color(), Some(Color::rgb(1.0, 1.0, 1.0)));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue