M12: Boards: copies and pastes of joined objects stay joined

This commit is contained in:
bflatastic
2026-10-24 02:32:07 +07:00
parent 7ad127f3dd
commit 30dceb9482
5 changed files with 71 additions and 0 deletions
+22
View File
@@ -79,6 +79,28 @@ impl BoardItem {
}
}
/// Point the layers this object refers to (a connector's ends, a label's connector) at their
/// copies in `map`; the others stay.
pub fn remap(&mut self, map: &std::collections::BTreeMap<NodeId, NodeId>) {
match self {
BoardItem::Connector(c) => {
for end in [&mut c.from, &mut c.to] {
if let End::Node { node, .. } = end
&& let Some(m) = map.get(node)
{
*node = *m;
}
}
}
BoardItem::Label { connector } => {
if let Some(m) = map.get(connector) {
*connector = *m;
}
}
_ => {}
}
}
pub fn kind_id(&self) -> &'static str {
match self {
BoardItem::Sticky { .. } => "sticky",
+4
View File
@@ -841,6 +841,10 @@ impl Document {
{
*component = *m;
}
// A connector (or label) copied with what it joins joins the copies.
if let Some(b) = c.board.as_mut() {
b.remap(map);
}
// A pattern tiling a layer of the copy tiles the copy's layer.
for p in c.fills.iter_mut().chain(c.strokes.iter_mut()) {
if let figcraft_color::PaintKind::Pattern(pt) = &mut p.kind
+12
View File
@@ -778,6 +778,18 @@ fn set_font_size(td: &mut TextData, size: f64) {
}
}
/// After layers were copied one by one (`map`: each original's copy), the copied connectors and
/// labels among `copies` refer to the copies of what they referred to.
pub(crate) fn rejoin(doc: &mut Document, copies: &[NodeId], map: &std::collections::BTreeMap<NodeId, NodeId>) {
for id in copies {
if doc.node(*id).is_some_and(|n| matches!(n.board, Some(BoardItem::Connector(_) | BoardItem::Label { .. })))
&& let Some(b) = doc.node_mut(*id).and_then(|n| n.board.as_mut())
{
b.remap(map);
}
}
}
// ---------------------------------------------------------------------------------------------
// Derived data (the normalize pass).
+29
View File
@@ -330,3 +330,32 @@ fn a_press_on_a_stickys_background_picks_and_drags_it() {
assert_eq!(node(&s, a).transform.translation().y, 300.0);
assert!(near(drawn(&s, c)[0], Point::new(200.0, 400.0)), "the connector followed the drag: {:?}", drawn(&s, c));
}
#[test]
fn a_copy_of_joined_objects_joins_the_copies() {
let mut s = board();
let a = id(&s.execute("board.sticky", &json!({"x": 0, "y": 0})).unwrap(), "id");
let b = id(&s.execute("board.sticky", &json!({"x": 400, "y": 0})).unwrap(), "id");
let c = id(&s.execute("board.connector", &json!({"from": a, "to": b, "label": "x"})).unwrap(), "id");
let label = crate::board::label_of(s.doc().unwrap(), c).unwrap();
s.execute("edit.duplicate", &json!({"ids": [a, b, c, label]})).unwrap();
let copies = s.selection();
assert_eq!(copies.len(), 4, "{copies:?}");
let k = copies.iter().find_map(|i| match node(&s, *i).board {
Some(BoardItem::Connector(k)) => Some(k),
_ => None,
});
let k = k.unwrap();
assert!(copies.contains(&k.from.node().unwrap()) && copies.contains(&k.to.node().unwrap()), "{k:?} {copies:?}");
// Copy and paste too.
s.execute("select.set", &json!({"ids": [a, b, c]})).unwrap();
s.execute("edit.copy", &json!({})).unwrap();
s.execute("edit.paste", &json!({})).unwrap();
let pasted = s.selection();
let k = pasted.iter().find_map(|i| match node(&s, *i).board {
Some(BoardItem::Connector(k)) => Some(k),
_ => None,
});
let k = k.unwrap();
assert!(pasted.contains(&k.from.node().unwrap()) && pasted.contains(&k.to.node().unwrap()), "{k:?} {pasted:?}");
}
+4
View File
@@ -494,6 +494,8 @@ fn duplicate(s: &mut Session, p: &Value) -> Result<Value> {
}
out.push(nid);
}
// Board connectors copied with what they join join the copies.
crate::board::rejoin(doc, &out, &ids.iter().copied().zip(out.iter().copied()).collect());
s.set_selection(out.clone());
s.ds_mut()?.last_duplicate = Some(Duplicated { sources: ids, copies: out.clone() });
Ok(json!({"ids": out}))
@@ -675,6 +677,8 @@ pub fn paste_into(s: &mut Session, parent: NodeId, at: Option<(f64, f64)>) -> Re
out.push(doc.paste_nodes(&nodes, &map, *root, into, None)?);
all.extend(map);
}
// Board connectors pasted with what they join join the pasted copies.
crate::board::rejoin(doc, &out, &all);
if let Some(src) = foreign {
crate::library::adopt_pasted(doc, src, clip.source_path.as_deref(), &all)?;
}