mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-29 12:56:47 -06:00
#921: a pasted image is added on top of the selected layer's fills; the others keep their variables
This commit is contained in:
@@ -83,7 +83,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
spec!(
|
||||
"image.place",
|
||||
"Place image",
|
||||
"{data (base64) | path, x?, y?, width?, height?, parent?, target? (an existing shape to fill instead of making a layer), mode? (fill|fit|crop|tile)} → {id, image, width, height, imageProfile (its embedded colour profile; null: taken as sRGB)}",
|
||||
"{data (base64) | path, x?, y?, width?, height?, parent?, target? (an existing layer: the image is added on top of its fills instead of making a layer), mode? (fill|fit|crop|tile)} → {id, image, width, height, imageProfile (its embedded colour profile; null: taken as sRGB)}",
|
||||
true,
|
||||
place_image
|
||||
),
|
||||
@@ -378,10 +378,10 @@ pub fn accepts_image_fill(kind: &NodeKind) -> bool {
|
||||
kind.is_shape() || kind.is_frame_like() || matches!(kind, NodeKind::Text(_) | NodeKind::BooleanOp { .. })
|
||||
}
|
||||
|
||||
/// Replace the fill list and detach variable bindings on paints that the image replaces.
|
||||
pub(crate) fn replace_with_image_fill(node: &mut Node, image: String, mode: ImageScaleMode) {
|
||||
node.fills = vec![Paint::image(image, mode)];
|
||||
node.bound.retain(|key, _| !matches!(super::lists::list_binding(key), Some((super::lists::BoundList::Fills, _, _))));
|
||||
/// Add an image fill on top of the layer's fills: the others keep their place and variable
|
||||
/// bindings; a linked fill style no longer describes the list, so the link goes.
|
||||
fn add_image_fill(node: &mut Node, image: String, mode: ImageScaleMode) {
|
||||
node.fills.push(Paint::image(image, mode));
|
||||
node.styles.fill = None;
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ pub fn place_image(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let (hash, _, _) = figcraft_format::add_image(doc, bytes).map_err(|e| bad(cmd, e.to_string()))?;
|
||||
let profile = figcraft_render::images::profile_name(&hash, doc.images.get(&hash));
|
||||
let node = doc.get_mut(id)?;
|
||||
replace_with_image_fill(node, hash.clone(), mode);
|
||||
add_image_fill(node, hash.clone(), mode);
|
||||
return Ok(json!({"id": id, "image": hash, "width": node.width, "height": node.height, "imageProfile": profile}));
|
||||
}
|
||||
let page = s.page()?;
|
||||
|
||||
@@ -196,7 +196,10 @@ fn cmd_v_fills_a_selected_oval_without_adding_a_layer() {
|
||||
let node = doc.node(oval).unwrap();
|
||||
assert_eq!(doc.node_count(), before);
|
||||
assert!(matches!(node.kind, figcraft_doc::NodeKind::Ellipse { .. }));
|
||||
assert!(matches!(node.fills[0].kind, figcraft_color::PaintKind::Image(_)));
|
||||
// Added on top: the oval's own fill stays under it.
|
||||
assert_eq!(node.fills.len(), original_fill.len() + 1);
|
||||
assert_eq!(node.fills[..original_fill.len()], original_fill[..]);
|
||||
assert!(matches!(node.fills.last().unwrap().kind, figcraft_color::PaintKind::Image(_)));
|
||||
assert_eq!((node.width, node.height), (120.0, 80.0));
|
||||
assert_eq!(w.app.session.selection(), vec![oval]);
|
||||
let png = figcraft_engine::cmd::file::export_bytes(doc, &[oval], figcraft_doc::ExportFormat::Png, 1.0).unwrap();
|
||||
@@ -209,7 +212,7 @@ fn cmd_v_fills_a_selected_oval_without_adding_a_layer() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pasting_an_image_detaches_the_replaced_fill_variable() {
|
||||
fn pasting_an_image_keeps_the_other_fills_variables() {
|
||||
let mut w = Win::new();
|
||||
let oval =
|
||||
NodeId(w.app.session.execute("shape.ellipse", &json!({"x": 200, "y": 100, "width": 120, "height": 80})).unwrap()["id"].as_u64().unwrap());
|
||||
@@ -221,12 +224,14 @@ fn pasting_an_image_detaches_the_replaced_fill_variable() {
|
||||
w.copy_image_elsewhere();
|
||||
w.cmd_v(CMD);
|
||||
let node = w.app.session.doc().unwrap().node(oval).unwrap();
|
||||
assert!(matches!(node.fills[0].kind, figcraft_color::PaintKind::Image(_)));
|
||||
assert!(node.bound.is_empty(), "the old colour variable must not remain on the image fill");
|
||||
assert!(matches!(node.fills.last().unwrap().kind, figcraft_color::PaintKind::Image(_)));
|
||||
assert!(matches!(node.fills[0].kind, figcraft_color::PaintKind::Solid { .. }));
|
||||
assert!(!node.bound.is_empty(), "the colour fill under the image keeps its variable");
|
||||
}
|
||||
|
||||
/// An image layer copied in FigCraft pastes as a layer, a selected shape or not.
|
||||
#[test]
|
||||
fn cmd_v_fills_a_selected_oval_with_an_image_copied_in_figcraft() {
|
||||
fn an_image_layer_copied_in_figcraft_pastes_as_a_layer() {
|
||||
let mut w = Win::new();
|
||||
w.copy_image_elsewhere();
|
||||
w.cmd_v(CMD);
|
||||
@@ -235,14 +240,15 @@ fn cmd_v_fills_a_selected_oval_with_an_image_copied_in_figcraft() {
|
||||
|
||||
let oval =
|
||||
NodeId(w.app.session.execute("shape.ellipse", &json!({"x": 200, "y": 100, "width": 120, "height": 80})).unwrap()["id"].as_u64().unwrap());
|
||||
let fills = w.app.session.doc().unwrap().node(oval).unwrap().fills.clone();
|
||||
let before = w.app.session.doc().unwrap().node_count();
|
||||
w.cmd_v(CMD);
|
||||
|
||||
let doc = w.app.session.doc().unwrap();
|
||||
assert_eq!(doc.node_count(), before);
|
||||
assert_eq!(doc.node_count(), before + 1);
|
||||
assert!(doc.contains(source));
|
||||
assert!(matches!(doc.node(oval).unwrap().fills[0].kind, figcraft_color::PaintKind::Image(_)));
|
||||
assert_eq!(w.app.session.selection(), vec![oval]);
|
||||
assert_eq!(doc.node(oval).unwrap().fills, fills);
|
||||
assert_ne!(w.app.session.selection(), vec![oval]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user