diff --git a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs index 5bf6cce032..f40b07d1c9 100644 --- a/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs @@ -108,6 +108,7 @@ impl MessageHandler> for GraphOperationMessage::NewArtboard { id, artboard } => { let mut modify_inputs = ModifyInputsContext::new(network_interface, responses); + let artboard_location = artboard.location; let artboard_layer = modify_inputs.create_artboard(id, artboard); network_interface.move_layer_to_stack(artboard_layer, LayerNodeIdentifier::ROOT_PARENT, 0, &[]); @@ -116,13 +117,33 @@ impl MessageHandler> for log::error!("Artboard not created"); return; }; + let document_metadata = network_interface.document_metadata(); + let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone(); if let NodeInput::Node { node_id, .. } = &primary_input { - if network_interface.is_layer(node_id, &[]) && !network_interface.is_artboard(node_id, &[]) { - network_interface.move_layer_to_stack(LayerNodeIdentifier::new(*node_id, network_interface), artboard_layer, 0, &[]); + if network_interface.is_artboard(node_id, &[]) { + // Nothing to do here: we have a stack full of artboards! + } else if network_interface.is_layer(node_id, &[]) { + // We have a stack of non-layer artboards. + for (insert_index, layer) in LayerNodeIdentifier::ROOT_PARENT.children(document_metadata).filter(|&layer| layer != artboard_layer).enumerate() { + // Parent the layer to our new artboard (retaining ordering) + responses.add(NodeGraphMessage::MoveLayerToStack { + layer, + parent: artboard_layer, + insert_index, + }); + // Apply a translation to prevent the content from shifting + responses.add(GraphOperationMessage::TransformChange { + layer, + transform: DAffine2::from_translation(-artboard_location.as_dvec2()), + transform_in: TransformIn::Local, + skip_rerender: true, + }); + } } else { + // We have some non layers (e.g. just a rectangle node). We disconnect the bottom input and connect it to the left input. network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]); - network_interface.set_input(&InputConnector::node(id, 0), primary_input, &[]); + network_interface.set_input(&InputConnector::node(artboard_layer.to_node(), 1), primary_input, &[]); } } responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] }); diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 3f1b120b1a..dd2ae0b142 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -762,4 +762,30 @@ mod test_artboard { has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((60, 10), (10, 12)), ArtboardLayoutDocument::new((70, 0), (10, 100))]).await; } + + #[tokio::test] + async fn first_artboard() { + let mut editor = EditorTestUtils::create(); + + editor.new_document().await; + // Put rectangles in before making the artboard + editor.drag_tool(ToolType::Rectangle, 10., 10., 20., 16., ModifierKeys::empty()).await; + editor.drag_tool(ToolType::Rectangle, 15., 15., 25., 25., ModifierKeys::empty()).await; + + // Put the artboard in + editor.drag_tool(ToolType::Artboard, 5., 5., 30., 10., ModifierKeys::empty()).await; + has_artboards(&mut editor, vec![ArtboardLayoutDocument::new((5, 5), (25, 5))]).await; + let document = editor.active_document(); + + // artboard + // ├── rectangle1 + // └── rectangle2 + let artboard = document.metadata().all_layers().next().unwrap(); + let rectangle2 = artboard.first_child(document.metadata()).unwrap(); + let rectangle1 = rectangle2.next_sibling(document.metadata()).unwrap(); + + // The document bounding boxes should remain the same (content shouldn't shift) + assert_eq!(document.metadata().bounding_box_document(rectangle1).unwrap(), [DVec2::new(10., 10.), DVec2::new(20., 16.)]); + assert_eq!(document.metadata().bounding_box_document(rectangle2).unwrap(), [DVec2::new(15., 15.), DVec2::new(25., 25.)]); + } } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index fc0e299082..709f14bb8b 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -542,7 +542,8 @@ mod test { } else if let Some(x) = dynamic.downcast_ref::>() { Some(x.output.clone()) } else { - panic!("cannot downcast type for introspection"); + warn!("cannot downcast type for introspection"); + None } } @@ -557,7 +558,7 @@ mod test { .iter() .filter_map(|inputs| inputs.get(Input::INDEX)) .filter_map(|input_monitor_node| runtime.executor.introspect(input_monitor_node).ok()) - .filter_map(Instrumented::downcast::) + .filter_map(Instrumented::downcast::) // Some might not resolve (e.g. generics that don't work properly) } pub fn grab_protonode_input(&self, path: &Vec, runtime: &NodeRuntime) -> Option