mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Merge pull request #4842 from dotCooCoo/fix/assemble-keeps-visibility
Outline Stroke, Expand: hidden and locked objects stay hidden and locked
This commit is contained in:
@@ -888,11 +888,13 @@ pub(crate) fn outline_strokes(d: &mut Document, brushes: &[vectorcraft_brush::Br
|
||||
}
|
||||
|
||||
/// A plain copy of path `l` (new ids, no transparency, name or mask) painted with `items`.
|
||||
/// The copy is shown and unlocked.
|
||||
pub(crate) fn styled_copy(d: &mut Document, l: &Node, items: Vec<AppearanceItem>) -> Node {
|
||||
let mut f = d.reid(l);
|
||||
f.appearance = Appearance { items, ..Default::default() }.into();
|
||||
f.opacity = 1.0;
|
||||
f.blend = BlendMode::Normal;
|
||||
(f.visible, f.locked, f.knockout_shape) = (true, false, false);
|
||||
f.name = None;
|
||||
f.extras_mut().mask = None;
|
||||
if let NodeKind::Path { live, .. } = &mut f.kind {
|
||||
@@ -903,7 +905,7 @@ pub(crate) fn styled_copy(d: &mut Document, l: &Node, items: Vec<AppearanceItem>
|
||||
|
||||
/// What stands for object `l` once its appearance became `parts` (paint order): the one part (in a
|
||||
/// group when it has effects of its own) or a group of them, with `l`'s transparency, name, opacity
|
||||
/// mask and effects. `None` without parts.
|
||||
/// mask, effects, and hidden, locked and knockout-shape states. `None` without parts.
|
||||
pub(crate) fn assemble(d: &mut Document, l: &Node, mut parts: Vec<Node>) -> Option<Node> {
|
||||
let root = match parts.len() {
|
||||
0 => return None,
|
||||
@@ -911,6 +913,7 @@ pub(crate) fn assemble(d: &mut Document, l: &Node, mut parts: Vec<Node>) -> Opti
|
||||
_ => Node::group(d.alloc_id(), parts.into_iter().map(Arc::new).collect()),
|
||||
};
|
||||
let mut root = with_transparency(d, root, l.opacity, l.blend);
|
||||
(root.visible, root.locked, root.knockout_shape) = (l.visible, l.locked, l.knockout_shape);
|
||||
root.name = l.name.clone();
|
||||
root.extras_mut().mask = l.extras().mask.clone();
|
||||
root.appearance.effects = l.appearance.effects.clone();
|
||||
|
||||
@@ -159,6 +159,35 @@ fn eight_strips_match_the_gradient_samples() {
|
||||
assert!(matches!(selected(&s).appearance.fill_paint(), Paint::Gradient(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expanding_a_group_keeps_its_hidden_and_locked_members_hidden_and_locked() {
|
||||
// A hidden stroked square with a gradient fill, a locked square with a pattern fill and a
|
||||
// plain square, grouped.
|
||||
let mut s = session();
|
||||
let pat = pattern_rect(&mut s, Rect::new(20.0, 100.0, 80.0, 160.0), false);
|
||||
let grad = gradient_rect(&mut s, Rect::new(20.0, 20.0, 80.0, 80.0), white_black(), true);
|
||||
let plain = NodeId(run(&mut s, "shape.rectangle", json!({"x": 100, "y": 20, "width": 40, "height": 40}))["id"].as_u64().unwrap());
|
||||
run(&mut s, "select.set", json!({"ids": [pat.0, grad.0, plain.0]}));
|
||||
run(&mut s, "object.group", json!({}));
|
||||
s.edit("t", |d, _| {
|
||||
d.node_mut(grad).unwrap().visible = false;
|
||||
d.node_mut(pat).unwrap().locked = true;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
run(&mut s, "object.expand", json!({}));
|
||||
let g = selected(&s);
|
||||
let ch = g.children().unwrap();
|
||||
assert_eq!(ch.len(), 3);
|
||||
assert!(!clip_parts(&ch[0]).1.is_empty() && ch[0].locked && ch[0].visible, "the pattern's tiles stay locked: {:?}", ch[0]);
|
||||
assert!(!ch[1].visible && !ch[1].locked, "the gradient square's art stays hidden: {:?}", ch[1]);
|
||||
assert!(ch[2].visible && !ch[2].locked);
|
||||
// Showing or unlocking the new art shows or unlocks all of it.
|
||||
for c in ch[..2].iter().flat_map(|c| c.children().unwrap().iter()) {
|
||||
c.walk(&mut |n| assert!(n.visible && !n.locked, "{n:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_radial_gradient_becomes_concentric_ellipses() {
|
||||
let mut s = session();
|
||||
|
||||
@@ -122,6 +122,36 @@ fn every_stroke_is_outlined_in_paint_order() {
|
||||
assert_eq!(d.layers[0].children().unwrap()[0].appearance.items.len(), 3);
|
||||
}
|
||||
|
||||
/// The art that stands for an outlined path keeps the path's hidden, locked and knockout-shape
|
||||
/// states, so a hidden member of a selected group stays hidden.
|
||||
#[test]
|
||||
fn outlined_art_keeps_the_hidden_locked_and_knockout_shape_states() {
|
||||
let mut s = session();
|
||||
// A filled and stroked square (a fill copy and an outline) and a stroked line (one outline).
|
||||
let sq = NodeId(s.execute("shape.rectangle", &json!({"x": 0, "y": 100, "width": 50, "height": 50})).unwrap()["id"].as_u64().unwrap());
|
||||
s.execute("paint.setFill", &json!({"color": "#ff0000"})).unwrap();
|
||||
s.execute("paint.setStroke", &json!({"color": "#000000"})).unwrap();
|
||||
let ln = line(&mut s, json!({"weight": 4}));
|
||||
s.execute("select.set", &json!({"ids": [sq.0, ln.0]})).unwrap();
|
||||
s.execute("object.group", &json!({})).unwrap();
|
||||
s.edit("t", |d, _| {
|
||||
d.node_mut(sq).unwrap().visible = false;
|
||||
let l = d.node_mut(ln).unwrap();
|
||||
l.locked = true;
|
||||
l.knockout_shape = true;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
let g = outline(&mut s);
|
||||
let ch = g.children().unwrap();
|
||||
assert_eq!(ch.len(), 2);
|
||||
let (sq, ln) = (&ch[0], &ch[1]);
|
||||
assert!(!sq.visible && !sq.locked, "the square's art stays hidden: {sq:?}");
|
||||
assert_eq!(sq.children().map(|c| c.len()), Some(2), "a fill copy and the outline");
|
||||
assert!(sq.children().unwrap().iter().all(|c| c.visible && !c.locked), "showing it shows all of it: {sq:?}");
|
||||
assert!(ln.visible && ln.locked && ln.knockout_shape, "the line's outline stays locked: {ln:?}");
|
||||
}
|
||||
|
||||
/// The live effect on a stroke item outlines that stroke (its own weight), not the top stroke.
|
||||
#[test]
|
||||
fn the_live_effect_outlines_the_stroke_it_sits_on() {
|
||||
|
||||
Reference in New Issue
Block a user