From 6595974a52844f6091a86d53c687c82aef34804d Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sat, 24 Oct 2026 09:52:54 +0700 Subject: [PATCH] #921: an image layer copied in FigCraft pastes as a layer; another app's image fills a selected layer only when it is newer than our copy --- crates/engine/src/cmd/edit.rs | 25 ++----------------------- crates/ui-egui/src/menus.rs | 24 ++++++++---------------- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index e1d6273f..60a96b31 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -27,7 +27,7 @@ pub fn specs() -> Vec { spec!( "edit.paste", "Paste", - "{parent?, x?, y? (page coordinates of the pasted top-left)} a single copied image fills a selected shape; a copied main component pastes as an instance of it within its file (cut ones, and pastes into other files, keep the component)", + "{parent?, x?, y? (page coordinates of the pasted top-left)} a copied main component pastes as an instance of it within its file (cut ones, and pastes into other files, keep the component)", true, paste ), @@ -688,32 +688,11 @@ pub fn paste_into(s: &mut Session, parent: NodeId, at: Option<(f64, f64)>) -> Re Ok(out) } -fn copied_image(s: &Session) -> Option<(String, ImageData)> { - let [(_, nodes)] = s.clipboard.nodes.as_slice() else { return None }; - let [node] = nodes.as_slice() else { return None }; - let [paint] = node.fills.as_slice() else { return None }; - let figcraft_color::PaintKind::Image(image) = &paint.kind else { return None }; - s.clipboard.images.get(&image.image).map(|data| (image.image.clone(), data.clone())) -} - fn paste(s: &mut Session, p: &Value) -> Result { - let sel = s.selection(); - if get(p, "parent").is_none() - && get(p, "x").is_none() - && get(p, "y").is_none() - && let [target] = sel.as_slice() - && s.doc()?.node(*target).is_some_and(|node| super::create::accepts_image_fill(&node.kind)) - && let Some((hash, data)) = copied_image(s) - { - let doc = s.doc_mut()?; - doc.images.entry(hash.clone()).or_insert(data); - let node = doc.get_mut(*target)?; - super::create::replace_with_image_fill(node, hash, figcraft_color::ImageScaleMode::Fill); - return Ok(json!({"ids": [target]})); - } let page = s.page()?; // Figma pastes into the selected frame (if one is selected), at the same position when it fits, // else in the frame's centre; otherwise in place on the page (or the viewport centre). + let sel = s.selection(); let doc = s.doc()?; let copy_local = s.ds()?.uid == s.clipboard.source_doc; let target = id(p, "parent").or_else(|| { diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 715f8c12..dede7cb7 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -57,22 +57,6 @@ pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) } return; } - let selected_layer = match app.session.selection().as_slice() { - [id] if at.is_none() - && app.session.doc().ok().and_then(|d| d.node(*id)).is_some_and(|n| figcraft_engine::cmd::create::accepts_image_fill(&n.kind)) => - { - Some(*id) - } - _ => None, - }; - if let Some(target) = selected_layer - && let Some((png, _, _)) = clipboard_png(app) - { - use base64::Engine as _; - let b64 = base64::engine::general_purpose::STANDARD.encode(png); - app.run("image.place", json!({"data": b64, "target": target})); - return; - } let sys_text = app.services.clipboard_get_text().or_else(|| event_text.map(str::to_string)).filter(|t| !t.is_empty()); let ours = sys_text.is_some() && sys_text == app.ui.last_copy; let (cx, cy) = at.or_else(|| app.session.ds().ok().map(|d| (d.view.cx, d.view.cy))).unwrap_or((0.0, 0.0)); @@ -83,6 +67,14 @@ pub fn paste(app: &mut FigApp, event_text: Option<&str>, at: Option<(f64, f64)>) } use base64::Engine as _; let b64 = base64::engine::general_purpose::STANDARD.encode(png); + // One selected layer that can have fills takes the image as its top fill. + if let [target] = app.session.selection()[..] + && at.is_none() + && app.session.doc().ok().and_then(|d| d.node(target)).is_some_and(|n| figcraft_engine::cmd::create::accepts_image_fill(&n.kind)) + { + app.run("image.place", json!({"data": b64, "target": target})); + return; + } // Centred in the view; Paste here puts its top-left at the point, as it does layers. let (x, y) = if at.is_some() { (cx, cy) } else { ((cx - f64::from(w) / 2.0).round(), (cy - f64::from(h) / 2.0).round()) }; app.run("file.import", json!({"data": b64, "name": "Pasted image.png", "x": x, "y": y}));