mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 07:13:46 +00:00
Merge pull request #6468 from dotCooCoo/fix/compound-member-copy-style
Edit: a copied compound member takes the compound's graphic style link
This commit is contained in:
@@ -264,6 +264,8 @@ impl Clipboard {
|
||||
.and_then(|p| *compound_bounds.entry(p.id).or_insert_with(|| p.geometric_bounds()));
|
||||
// A compound's member or piece copied on its own takes the compound's look (its paint,
|
||||
// transparency, opacity mask, pixel alignment and fill rule), so it pastes as it looked.
|
||||
// It is linked to the compound's graphic style while it still has that style's look
|
||||
// (a gradient pinned to the compound's bounds does not), else to none.
|
||||
let inherit_appearance = |mut piece: Node| {
|
||||
if let Some(parent) = compound_parent
|
||||
&& let (NodeKind::Compound { rule: compound_rule, .. }, NodeKind::Path { rule, .. }) = (&parent.kind, &mut piece.kind)
|
||||
@@ -276,11 +278,21 @@ impl Clipboard {
|
||||
piece.opacity = parent.opacity;
|
||||
piece.blend = parent.blend;
|
||||
piece.isolate = parent.isolate;
|
||||
piece.knockout = parent.knockout;
|
||||
piece.knockout_shape = parent.knockout_shape;
|
||||
let (mask, pixel_aligned) = (parent.extras().mask.clone(), parent.extras().pixel_aligned);
|
||||
let mut extras = piece.extras_mut();
|
||||
extras.mask = mask;
|
||||
extras.pixel_aligned = pixel_aligned;
|
||||
{
|
||||
let mut extras = piece.extras_mut();
|
||||
extras.mask = mask;
|
||||
extras.pixel_aligned = pixel_aligned;
|
||||
}
|
||||
let link = parent
|
||||
.extras()
|
||||
.graphic_style
|
||||
.and_then(|g| d.graphic_style_by_id(g))
|
||||
.filter(|g| super::style::in_sync(&piece, g))
|
||||
.map(|g| g.id);
|
||||
piece.extras_mut().graphic_style = link;
|
||||
}
|
||||
piece
|
||||
};
|
||||
|
||||
@@ -166,7 +166,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Copy",
|
||||
["Edit"],
|
||||
Some("Cmd+C"),
|
||||
"{selectedArtboards?: [n…] (the artboards the Artboard tool had selected, which the journal records for a replay; it runs artboard.copy with any tool chosen), art?: bool (default: the Artboard tool's moveArt option; the journal records it beside selectedArtboards), lockedAndHidden?: bool (the Artboard tool chosen, as artboard.copy)} the selection with what it uses (a compound-path member selected on its own, whole or in part, with Direct or Group Selection, is copied as an ordinary path with the compound's paint, transparency, opacity mask and fill rule, a gradient keeping the compound's fit; the compound is copied when it is selected, or when every member is selected whole outside isolation mode), and the selected ruler guides (pasted on the layer new art goes to, moved as the objects are; alone, where they were); the Artboard tool chosen: artboard.copy; with an object of a symbol instance's art selected, the instance is copied whole → {copied, guides}",
|
||||
"{selectedArtboards?: [n…] (the artboards the Artboard tool had selected, which the journal records for a replay; it runs artboard.copy with any tool chosen), art?: bool (default: the Artboard tool's moveArt option; the journal records it beside selectedArtboards), lockedAndHidden?: bool (the Artboard tool chosen, as artboard.copy)} the selection with what it uses (a compound-path member selected on its own, whole or in part, with Direct or Group Selection, is copied as an ordinary path with the compound's paint, transparency, opacity mask, fill rule and graphic style link (while it has that style's look), a gradient keeping the compound's fit; the compound is copied when it is selected, or when every member is selected whole outside isolation mode), and the selected ruler guides (pasted on the layer new art goes to, moved as the objects are; alone, where they were); the Artboard tool chosen: artboard.copy; with an object of a symbol instance's art selected, the instance is copied whole → {copied, guides}",
|
||||
has_selection_guides_or_artboard_tool,
|
||||
copy
|
||||
),
|
||||
|
||||
@@ -604,6 +604,66 @@ fn a_copied_member_with_its_own_appearance_takes_the_compounds() {
|
||||
assert_eq!(s.clipboard.nodes[0].appearance, compound_look);
|
||||
}
|
||||
|
||||
/// A member copied or cut on its own, whole or in part, takes the compound's graphic style link
|
||||
/// with its look, or no link when the compound has none or the member no longer has the style's
|
||||
/// look (a gradient pinned to the compound's bounds); the style the member had before Make Compound
|
||||
/// Path does not go with it.
|
||||
#[test]
|
||||
fn a_copied_member_takes_the_compounds_graphic_style() {
|
||||
let style_of = |s: &Session, id: NodeId| s.doc().unwrap().doc.node(id).unwrap().extras().graphic_style;
|
||||
let clipboard_styles = |s: &Session| s.clipboard.graphic_styles.iter().map(|g| g.name.clone()).collect::<Vec<_>>();
|
||||
for (compound_styled, gradient) in [(false, false), (true, false), (true, true)] {
|
||||
let mut s = Session::new();
|
||||
run(&mut s, "file.new", json!({"width": 200, "height": 200}));
|
||||
let m0 = rect(&mut s, 10.0, 10.0, 40.0, 40.0);
|
||||
run(&mut s, "paint.setFill", json!({"ids": [m0.0], "color": "#00ff00"}));
|
||||
run(&mut s, "select.set", json!({"ids": [m0.0]}));
|
||||
run(&mut s, "graphicStyle.new", json!({"name": "Member"}));
|
||||
let member_style = style_of(&s, m0);
|
||||
assert!(member_style.is_some(), "the path is linked to the style made from it");
|
||||
let m1 = rect(&mut s, 60.0, 10.0, 40.0, 40.0);
|
||||
run(&mut s, "select.set", json!({"ids": [m0.0, m1.0]}));
|
||||
let compound = NodeId(run(&mut s, "object.compoundPath.make", json!({}))["id"].as_u64().unwrap());
|
||||
let fill =
|
||||
if gradient { json!({"ids": [compound.0], "gradient": {"kind": "linear"}}) } else { json!({"ids": [compound.0], "color": "#ff0000"}) };
|
||||
run(&mut s, "paint.setFill", fill);
|
||||
assert_eq!(style_of(&s, m0), member_style, "the member keeps its old link inside the compound");
|
||||
let compound_style = if compound_styled {
|
||||
run(&mut s, "select.set", json!({"ids": [compound.0]}));
|
||||
run(&mut s, "graphicStyle.new", json!({"name": "Compound"}));
|
||||
style_of(&s, compound)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
assert_eq!(compound_style.is_some(), compound_styled);
|
||||
// A gradient fitted to the compound is pinned to its bounds on the copy, which is no longer
|
||||
// the style's look.
|
||||
let link = compound_style.filter(|_| !gradient);
|
||||
let expected: Vec<String> = if link.is_some() { vec!["Compound".into()] } else { vec![] };
|
||||
let case = format!("compound styled: {compound_styled}, gradient: {gradient}");
|
||||
|
||||
s.select_tool("groupSelection", vectorcraft_engine::ViewInfo::default()).unwrap();
|
||||
run(&mut s, "select.set", json!({"ids": [m0.0]}));
|
||||
run(&mut s, "edit.copy", json!({}));
|
||||
assert_eq!(s.clipboard.nodes[0].id, m0);
|
||||
assert_eq!(s.clipboard.nodes[0].extras().graphic_style, link, "whole member, {case}");
|
||||
assert_eq!(clipboard_styles(&s), expected, "whole member, {case}");
|
||||
|
||||
run(&mut s, "select.segments", json!({"id": m0.0, "segments": [[0, 0]]}));
|
||||
run(&mut s, "edit.copy", json!({}));
|
||||
assert_eq!(s.clipboard.nodes.len(), 1);
|
||||
assert_eq!(s.clipboard.nodes[0].extras().graphic_style, link, "one segment, {case}");
|
||||
assert_eq!(clipboard_styles(&s), expected, "one segment, {case}");
|
||||
|
||||
run(&mut s, "select.set", json!({"ids": [m0.0]}));
|
||||
run(&mut s, "edit.cut", json!({}));
|
||||
assert_eq!(members_of(&s, compound), Some(vec![m1]));
|
||||
let pasted = NodeId(run(&mut s, "edit.pasteInPlace", json!({}))["ids"][0].as_u64().unwrap());
|
||||
assert_eq!(style_of(&s, pasted), link, "Cut and Paste in Place, {case}");
|
||||
check_session(&s).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_segment_of_every_member_picked_copies_and_cuts_the_compound() {
|
||||
let mut s = Session::new();
|
||||
|
||||
+5
-4
@@ -1721,16 +1721,17 @@ overrides selected anchors or guides. Every explicit id is validated before any
|
||||
or unknown id leaves the document unchanged. An empty `ids` array does nothing. Layers themselves
|
||||
are kept, and selecting an ancestor together with its descendants deletes that subtree once.
|
||||
Without `ids`, direct-selected anchors or selected ruler guides retain their usual Clear behavior.
|
||||
With `ids`, `guides: true` also deletes the selected ruler guides. The command rejects a `guides` value that is not a
|
||||
boolean.
|
||||
|
||||
`edit.copy` takes a compound-path member selected on its own (whole or in part, with Direct or Group Selection) as an
|
||||
ordinary path with the compound's paint, transparency, opacity mask and fill rule. A gradient fitted to the compound
|
||||
keeps the compound's fit. Copy takes the whole compound when the compound itself is selected, or when every member is
|
||||
ordinary path with the compound's paint, transparency, opacity mask and fill rule. The path is linked to the compound's
|
||||
graphic style while it has that style's look, and otherwise to no style. A gradient fitted to the compound keeps the compound's fit. Copy takes the whole compound when the compound itself is selected, or when every member is
|
||||
selected whole outside isolation mode, which includes the one member of a compound of one. In an isolated compound
|
||||
path each member is taken as itself. `edit.cut` removes the same objects it copies, so cutting one member leaves the
|
||||
other members in the compound, a compound of one member included. When Clear deletes every anchor or segment of every
|
||||
member, it also removes the compound. With a copied member still selected, `edit.pasteInFront` puts the path into the
|
||||
compound, just above the member.
|
||||
With `ids`, `guides: true` also deletes the selected ruler guides. The command rejects a `guides` value that is not a
|
||||
boolean.
|
||||
|
||||
`object.setProps` with `visible: false` or `locked: true` deselects every selected object that is hidden or
|
||||
locked after the call: the selected objects it hides or locks, selected objects inside them, and selected objects
|
||||
|
||||
Reference in New Issue
Block a user