mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 06:01:18 +00:00
Guide clipboard: guides alone stay off the system clipboard; test two pastes and layers
Copying only ruler guides published an empty SVG and text to the system clipboard, replacing what other apps put there. The clipboard now offers other apps nothing without art. Tests cover two pastes adding guides of their own on the current layer (following its visibility) and the SVG carrying no guides.
This commit is contained in:
@@ -103,6 +103,12 @@ impl Clipboard {
|
||||
self.nodes.is_empty() && self.artboards.is_empty() && self.guides.is_empty()
|
||||
}
|
||||
|
||||
/// Does it hold art (objects or artboards) for other apps? Ruler guides stay in VectorCraft:
|
||||
/// guides alone put nothing on the system clipboard (#3226).
|
||||
pub fn has_art(&self) -> bool {
|
||||
!self.nodes.is_empty() || !self.artboards.is_empty()
|
||||
}
|
||||
|
||||
/// Objects `roots` of `st`'s document with their resources, source artboard and layers.
|
||||
pub(crate) fn copy(st: &DocState, roots: &[NodeId]) -> Self {
|
||||
let d = &*st.doc;
|
||||
@@ -198,7 +204,7 @@ impl Clipboard {
|
||||
|
||||
/// The objects as standalone SVG (`None` when there are none).
|
||||
pub fn svg(&self) -> Option<String> {
|
||||
if self.is_empty() {
|
||||
if !self.has_art() {
|
||||
return None;
|
||||
}
|
||||
let d = self.to_document();
|
||||
|
||||
@@ -286,7 +286,7 @@ impl Clipboard {
|
||||
|
||||
/// The MIME types of [`Self::flavours`], without making them.
|
||||
pub fn flavour_types(&self, svg: bool, pdf: bool) -> Vec<&'static str> {
|
||||
if self.is_empty() {
|
||||
if !self.has_art() {
|
||||
return vec![];
|
||||
}
|
||||
let text = (svg && self.image_bytes() <= COPY_TEXT_IMAGE_BYTES) || self.nodes.iter().all(type_only);
|
||||
|
||||
@@ -88,3 +88,44 @@ fn guides_paste_in_place_onto_another_artboard_and_cut_removes_them() {
|
||||
run(&mut s, "select.none", json!({}));
|
||||
assert!(s.execute("edit.copy", &json!({})).is_err());
|
||||
}
|
||||
|
||||
/// #3226 review: each paste adds guides of its own (a guide is its place and layer; there is no
|
||||
/// id to share), on the layer new art goes to, so they take that layer's colour, visibility and
|
||||
/// lock; and the SVG put on the system clipboard carries no guides.
|
||||
#[test]
|
||||
fn two_pastes_add_their_own_guides_on_the_current_layer_and_svg_has_none() {
|
||||
let mut s = Session::new();
|
||||
run(&mut s, "file.new", json!({"width": 400, "height": 300}));
|
||||
run(&mut s, "guide.add", json!({"vertical": true, "pos": 120}));
|
||||
run(&mut s, "guide.select", json!({"indexes": [0]}));
|
||||
run(&mut s, "edit.copy", json!({}));
|
||||
// Guides alone: no art for the system clipboard's SVG.
|
||||
assert!(s.clipboard_svg().is_none(), "guides alone put no SVG on the system clipboard");
|
||||
assert!(s.clipboard.flavour_types(true, true).is_empty(), "nor any other format (it keeps what other apps put there)");
|
||||
let layer = NodeId(run(&mut s, "layer.new", json!({}))["id"].as_u64().unwrap());
|
||||
run(&mut s, "edit.paste", json!({}));
|
||||
assert_eq!(s.doc().unwrap().selection.guides, [1], "the first paste selects its own guide");
|
||||
run(&mut s, "edit.paste", json!({}));
|
||||
assert_eq!(s.doc().unwrap().selection.guides, [2], "the second paste selects its own guide");
|
||||
let g = guides(&s);
|
||||
assert_eq!(g.len(), 3, "two pastes, two more guides: {g:?}");
|
||||
assert_eq!((g[1].2, g[2].2), (Some(layer), Some(layer)), "on the layer new art goes to");
|
||||
// Moving the second pasted guide leaves the first where it is.
|
||||
run(&mut s, "guide.move", json!({"index": 2, "pos": 200}));
|
||||
assert_eq!(guides(&s).iter().map(|g| g.1).collect::<Vec<_>>(), [120.0, 120.0, 200.0]);
|
||||
// Hiding that layer hides the guides pasted on it: they follow its visibility (and lock).
|
||||
run(&mut s, "layer.setProps", json!({"id": layer.0, "visible": false}));
|
||||
let st = s.doc().unwrap();
|
||||
let shown: Vec<bool> = st.doc.guides.iter().map(|g| st.doc.guide_shown(g)).collect();
|
||||
assert_eq!(shown, [true, false, false]);
|
||||
// With an object, the SVG holds the object and no guide.
|
||||
run(&mut s, "layer.setProps", json!({"id": layer.0, "visible": true}));
|
||||
let rect = run(&mut s, "shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20}))["id"].as_u64().unwrap();
|
||||
run(&mut s, "select.set", json!({"ids": [rect]}));
|
||||
run(&mut s, "guide.select", json!({"indexes": [0], "toggle": true}));
|
||||
let r = run(&mut s, "edit.copy", json!({}));
|
||||
assert_eq!(r["guides"], 1);
|
||||
let svg = s.clipboard_svg().unwrap();
|
||||
assert!(svg.contains("<rect") || svg.contains("<path"), "the object is there");
|
||||
assert!(!svg.to_lowercase().contains("guide"), "no guide in the SVG: {svg}");
|
||||
}
|
||||
|
||||
@@ -124,6 +124,11 @@ impl VectorcraftApp {
|
||||
/// ([`Self::write_formats_before_text_input`]). Once they are written, the status bar shows
|
||||
/// the first of the PNG's and PDF's warnings.
|
||||
pub(crate) fn publish_clipboard(&mut self) {
|
||||
// Ruler guides copied alone stay in VectorCraft: the system clipboard keeps what other
|
||||
// apps put there (#3226).
|
||||
if !self.session.clipboard.has_art() {
|
||||
return;
|
||||
}
|
||||
if self.services.system_clipboard.is_none() {
|
||||
if self.session.prefs.copy_as_svg {
|
||||
self.clipboard_out = self.session.clipboard_svg();
|
||||
|
||||
Reference in New Issue
Block a user