mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-30 05:16:56 -06:00
Merge branch 'rb-554' into batch/t6
This commit is contained in:
@@ -781,6 +781,23 @@ impl Document {
|
||||
self.paste_nodes(&src_nodes, &map, id, parent, index)
|
||||
}
|
||||
|
||||
/// Deep-copy each of `ids`' subtrees to the end of `parent`, in order, returning the new root
|
||||
/// ids. One id map covers every copy, so references between the copied subtrees (a navigation
|
||||
/// from one copied frame to another) point at the copies.
|
||||
pub fn duplicate_all_into(&mut self, ids: &[NodeId], parent: NodeId) -> DocResult<Vec<NodeId>> {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut trees = Vec::with_capacity(ids.len());
|
||||
for &id in ids {
|
||||
let src_nodes: Vec<Node> = self.subtree(id).into_iter().filter_map(|i| self.node(i).cloned()).collect();
|
||||
for n in &src_nodes {
|
||||
let nid = self.alloc_id();
|
||||
map.insert(n.id, nid);
|
||||
}
|
||||
trees.push((id, src_nodes));
|
||||
}
|
||||
trees.into_iter().map(|(id, src_nodes)| self.paste_nodes(&src_nodes, &map, id, parent, None)).collect()
|
||||
}
|
||||
|
||||
/// Insert copies of `nodes` (a subtree rooted at `root`, from this or another document) with ids
|
||||
/// remapped by `map`. Interactions, instance references and pattern sources pointing inside the
|
||||
/// subtree are remapped too.
|
||||
|
||||
@@ -97,11 +97,13 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
let doc = s.doc_mut()?;
|
||||
let name = format!("Copy of {}", doc.get(src)?.name);
|
||||
let np = doc.add_page(&name);
|
||||
// Each layer and its copy (also those in sections), so flows can follow their start frames.
|
||||
// One copy with one id map, so navigation between the copied frames points at the copies;
|
||||
// then each layer and its copy (also those in sections), so flows can follow their start frames.
|
||||
let children = doc.get(src)?.children.clone();
|
||||
let copied = doc.duplicate_all_into(&children, np)?;
|
||||
let mut copies = Vec::new();
|
||||
for c in doc.get(src)?.children.clone() {
|
||||
let copy = doc.duplicate_into(c, np, None)?;
|
||||
copies.extend(doc.subtree(c).into_iter().zip(doc.subtree(copy)));
|
||||
for (c, copy) in children.iter().zip(&copied) {
|
||||
copies.extend(doc.subtree(*c).into_iter().zip(doc.subtree(*copy)));
|
||||
}
|
||||
// The copy keeps the page's colour, flows (pointing at the copied frames), prototype settings and guides.
|
||||
if let NodeKind::Page { background, flows, prototype, guides } = doc.get(src)?.kind.clone()
|
||||
|
||||
@@ -1588,6 +1588,8 @@ mod open_issues_tests;
|
||||
#[cfg(test)]
|
||||
mod outline_options_tests;
|
||||
#[cfg(test)]
|
||||
mod page_copy_navigation_tests;
|
||||
#[cfg(test)]
|
||||
mod page_target_tests;
|
||||
#[cfg(test)]
|
||||
mod paint_reorder_tests;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//! Duplicating a page copies its prototype connections: navigation between two frames that are both
|
||||
//! on the page leads between the copies, while a destination outside the copied contents is kept.
|
||||
|
||||
use figcraft_doc::NodeId;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use crate::Session;
|
||||
use crate::params::id_of;
|
||||
|
||||
fn ex(s: &mut Session, cmd: &str, p: Value) -> Value {
|
||||
s.execute(cmd, &p).unwrap_or_else(|e| panic!("{cmd}: {e}"))
|
||||
}
|
||||
|
||||
fn frame(s: &mut Session, x: f64) -> NodeId {
|
||||
id_of(&ex(s, "frame.create", json!({"x": x, "y": 0, "width": 100, "height": 100}))["id"]).unwrap()
|
||||
}
|
||||
|
||||
fn dest(s: &Session, id: NodeId) -> NodeId {
|
||||
s.doc().unwrap().node(id).unwrap().interactions[0].actions[0].dest().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_copy_navigation_uses_the_copied_destination() {
|
||||
let mut s = Session::new();
|
||||
let first = frame(&mut s, 0.0);
|
||||
let second = frame(&mut s, 200.0);
|
||||
ex(&mut s, "proto.interaction.add", json!({"id": first, "action": {"type": "navigate", "dest": second}}));
|
||||
let copy = id_of(&ex(&mut s, "page.duplicate", json!({}))["id"]).unwrap();
|
||||
let children = s.doc().unwrap().node(copy).unwrap().children.clone();
|
||||
assert_eq!(children.len(), 2);
|
||||
assert_eq!(dest(&s, children[0]), children[1]);
|
||||
let visited = Default::default();
|
||||
assert_eq!(crate::proto_run::navigate_target(s.doc().unwrap(), dest(&s, children[0]), &visited), Some(children[1]));
|
||||
assert_eq!(dest(&s, first), second, "the original keeps its destination");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frame_copy_keeps_an_outside_destination() {
|
||||
let mut s = Session::new();
|
||||
let first = frame(&mut s, 0.0);
|
||||
let second = frame(&mut s, 200.0);
|
||||
ex(&mut s, "proto.interaction.add", json!({"id": first, "action": {"type": "navigate", "dest": second}}));
|
||||
ex(&mut s, "select.set", json!({"ids": [first]}));
|
||||
ex(&mut s, "edit.duplicate", json!({}));
|
||||
let copy = s.selection()[0];
|
||||
assert_ne!(copy, first);
|
||||
assert_eq!(dest(&s, copy), second);
|
||||
}
|
||||
Reference in New Issue
Block a user