mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 18:36:44 -06:00
M1: copying text layers puts their text on the clipboard (#808)
edit.copy and edit.cut mirror a selection made only of text layers to the system clipboard as plain text, one layer per line, instead of SVG. Other selections stay SVG; pasting back into FigCraft still pastes the copied layers. Closes #808 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
d8ff1441b1
commit
b778ba17cd
@@ -206,12 +206,13 @@ pub fn invoke(app: &mut FigApp, id: &str) {
|
||||
"collab.publish" => crate::live::open_publish(app),
|
||||
"edit.copy" | "edit.cut" => {
|
||||
let ids = app.session.selection();
|
||||
// SVG for other apps, taken before a cut removes the layers.
|
||||
let svg = app.session.doc().ok().and_then(|d| figcraft_svg_export(d, &ids));
|
||||
// For other apps: text layers' text, other layers as SVG; taken before a cut removes them.
|
||||
let doc = app.session.doc().ok();
|
||||
let mirror = doc.and_then(|d| text_layers_text(d, &ids).or_else(|| figcraft_svg_export(d, &ids)));
|
||||
app.run(id, json!({}));
|
||||
// Mirror to the system clipboard (SVG text, else a marker) and remember it, so a later
|
||||
// paste knows whether the clipboard still holds our layers.
|
||||
let text = svg.unwrap_or_else(|| format!("figcraft://clipboard/{}", ids.len()));
|
||||
// Mirror to the system clipboard (text or SVG, else a marker) and remember it, so a
|
||||
// later paste knows whether the clipboard still holds our layers.
|
||||
let text = mirror.unwrap_or_else(|| format!("figcraft://clipboard/{}", ids.len()));
|
||||
app.services.clipboard_set_text(&text);
|
||||
app.ui.last_copy = Some(text);
|
||||
}
|
||||
@@ -239,6 +240,13 @@ fn export_frames_pdf(app: &mut FigApp) {
|
||||
});
|
||||
}
|
||||
|
||||
/// The text of a selection made only of text layers, one layer per line (None for any other
|
||||
/// selection, or when there is no text).
|
||||
fn text_layers_text(doc: &figcraft_doc::Document, ids: &[figcraft_doc::NodeId]) -> Option<String> {
|
||||
let texts: Option<Vec<&str>> = ids.iter().map(|i| doc.node(*i).and_then(|n| n.kind.text()).map(|t| t.characters.as_str())).collect();
|
||||
texts.map(|t| t.join("\n")).filter(|t| !t.is_empty())
|
||||
}
|
||||
|
||||
fn figcraft_svg_export(doc: &figcraft_doc::Document, ids: &[figcraft_doc::NodeId]) -> Option<String> {
|
||||
let bytes = figcraft_engine::cmd::file::export_bytes(doc, ids, figcraft_doc::ExportFormat::Svg, 1.0).ok()?;
|
||||
String::from_utf8(bytes).ok()
|
||||
|
||||
@@ -141,6 +141,24 @@ fn text_copied_elsewhere_after_our_copy_still_pastes_as_text() {
|
||||
assert_eq!(p.count(text), 1, "newer text from elsewhere becomes a text layer");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copying_text_layers_puts_their_text_on_the_clipboard() {
|
||||
// #808: other apps get a text layer's text, not its SVG; FigCraft still pastes the layers.
|
||||
let mut p = Page::new();
|
||||
let a = p.app.session.execute("text.create", &json!({"text": "Hello", "x": 0, "y": 200})).unwrap();
|
||||
let b = p.app.session.execute("text.create", &json!({"text": "World", "x": 0, "y": 300})).unwrap();
|
||||
p.app.session.execute("select.set", &json!({"ids": [a["id"], b["id"]]})).unwrap();
|
||||
p.frame(vec![]);
|
||||
p.copy();
|
||||
assert_eq!(p.clipboard, "Hello\nWorld", "one line per text layer");
|
||||
p.paste();
|
||||
assert_eq!(p.count(text), 4, "the copied text layers were pasted, not one layer of their text");
|
||||
let r = p.app.session.doc().unwrap().nodes().find(|n| rect(&n.kind)).map(|n| n.id.0).unwrap();
|
||||
p.app.session.execute("select.set", &json!({"ids": [r, a["id"]]})).unwrap();
|
||||
p.copy();
|
||||
assert!(p.clipboard.contains("<svg"), "a selection with other layers stays SVG");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shift_cmd_v_pastes_over_once_and_no_text() {
|
||||
for foreign in [false, true] {
|
||||
|
||||
Reference in New Issue
Block a user