Code cleanup and refactor for generalized layers (#1738)

* Move functions to messages to fix undo bugs for UnGroup and Group

* Copy+Paste for generalized layer nodes

* Fix MoveSelectedLayersTo and GroupSelectedLayers by extracting functions into messages

* Fix tests, replace FrontendMessage:TriggerPaste with PortfolioMessage::PasteIntoFolder

* Formatting

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
adamgerhant 2024-05-04 22:11:48 -07:00 committed by GitHub
parent 8d83fa7079
commit 4c3856833b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 507 additions and 440 deletions

View File

@ -1,3 +1,4 @@
use super::utility_types::clipboards::Clipboard;
use super::utility_types::error::EditorError;
use super::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, OptionBoundsSnapping, OptionPointSnapping, SnappingOptions, SnappingState};
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
@ -5,9 +6,7 @@ use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH};
use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
@ -279,36 +278,16 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
}
DocumentMessage::DuplicateSelectedLayers => {
self.backup(responses);
for layer_ancestors in self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata)) {
let Some(layer) = layer_ancestors.last().copied() else { continue };
let Some(parent) = layer.parent(&self.metadata) else { continue };
let Some(node) = self.network().nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).and_then(|input| input.as_node()) else {
continue;
};
let parent = self.new_layer_parent(false);
let calculated_insert_index = self.get_calculated_insert_index(parent);
let nodes = NodeGraphMessageHandler::copy_nodes(
self.network(),
&self
.network()
.upstream_flow_back_from_nodes(vec![node], FlowType::UpstreamFlow)
.enumerate()
.map(|(index, (_, node_id))| (node_id, NodeId(index as u64)))
.collect(),
)
.collect();
let id = NodeId(generate_uuid());
let selected_layer_index = parent.children(self.metadata()).collect::<Vec<_>>().iter().position(|&sibling| sibling == layer).unwrap_or(0);
let insert_index = if (selected_layer_index as i64 - 1) < 0 { -1 } else { selected_layer_index as isize };
responses.add(GraphOperationMessage::NewCustomLayer {
id,
nodes,
parent,
insert_index,
alias: String::new(),
});
}
responses.add(DocumentMessage::StartTransaction);
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
responses.add(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal,
parent,
insert_index: calculated_insert_index,
});
}
DocumentMessage::FlipSelectedLayers { flip_axis } => {
self.backup(responses);
@ -356,6 +335,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
responses.add(OverlaysMessage::Draw);
}
DocumentMessage::GroupSelectedLayers => {
responses.add(DocumentMessage::StartTransaction);
let parent = self
.metadata()
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), false)
@ -390,8 +371,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
continue;
}
// Disconnect above and below the old layer location
self.disconnect_node(layer, responses);
responses.add(NodeGraphMessage::DisconnectLayerFromStack {
node_id: layer.to_node(),
reconnect_to_sibling: true,
});
// Move disconnected node to folder
let folder_position = self
@ -401,118 +384,48 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
.expect("Current folder should always exist")
.metadata
.position;
let Some(layer_to_move_node_mut) = self.network.nodes.get_mut(&layer.to_node()) else {
return;
};
DocumentMessageHandler::disconnect_input(layer_to_move_node_mut, 0);
layer_to_move_node_mut.metadata.position = folder_position;
responses.add(NodeGraphMessage::SetNodePosition {
node_id: layer.to_node(),
position: folder_position,
});
// Insert node right above the folder
// TODO: Use insert layer between message
let Some((folder_downstream_node_id, folder_downstream_input_index)) = DocumentMessageHandler::get_downstream_node(&self.network, &self.metadata, first_unselected_parent_folder)
else {
log::error!("Downstream node should always exist when inserting layer");
return;
};
let downstream_input = self
.network
.nodes
.get_mut(&folder_downstream_node_id)
.and_then(|node| node.inputs.get_mut(folder_downstream_input_index));
let Some(NodeInput::Node { node_id, .. }) = downstream_input else {
log::error!("Downstream node should have a node input");
return;
};
*node_id = layer.to_node();
responses.add(GraphOperationMessage::InsertNodeBetween {
post_node_id: folder_downstream_node_id,
post_node_input_index: folder_downstream_input_index,
insert_node_output_index: 0,
insert_node_id: layer.to_node(),
insert_node_input_index: 0,
pre_node_output_index: 0,
pre_node_id: first_unselected_parent_folder.to_node(),
});
// Connect layer primary input to parent folder
let Some(layer_node_input) = self.network.nodes.get_mut(&layer.to_node()).and_then(|node| node.inputs.get_mut(0)) else {
log::error!("Layer should always have primary input");
return;
};
*layer_node_input = NodeInput::node(first_unselected_parent_folder.to_node(), 0);
let upstream_shift = IVec2::new(0, 3);
let mut modify_inputs = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
modify_inputs.shift_upstream(first_unselected_parent_folder.to_node(), upstream_shift, true);
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: first_unselected_parent_folder.to_node(),
shift: IVec2::new(0, 3),
shift_self: true,
});
}
let calculated_insert_index = parent.children(self.metadata()).enumerate().find_map(|(index, direct_child)| {
if self.selected_nodes.selected_layers(self.metadata()).any(|selected| selected == direct_child) {
return Some(index as isize);
}
for descendant in direct_child.descendants(self.metadata()) {
if self.selected_nodes.selected_layers(self.metadata()).any(|selected| selected == descendant) {
return Some(index as isize);
}
}
None
});
let calculated_insert_index = self.get_calculated_insert_index(parent);
let folder_id = NodeId(generate_uuid());
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::NewCustomLayer {
id: folder_id,
nodes: HashMap::new(),
parent,
insert_index: calculated_insert_index.unwrap_or(-1),
insert_index: calculated_insert_index,
alias: String::new(),
});
// Create a vec of nodes to move with all selected layers in the parent layer child stack, as well as each non layer sibling directly upstream of the selected layer
let mut nodes_to_move = Vec::new();
responses.add(GraphOperationMessage::MoveSelectedSiblingsToChild { new_parent: folder_id });
// Skip over horizontal non layer node chain that feeds into parent
let Some(mut current_stack_node_id) = parent.first_child(&self.metadata).and_then(|current_stack_node| Some(current_stack_node.to_node())) else {
log::error!("Folder should always have child");
return;
};
let current_stack_node_id = &mut current_stack_node_id;
loop {
let mut current_stack_node = self.network.nodes.get(current_stack_node_id).expect("Current stack node id should always be a node");
// Check if the current stack node is a selected layer
if self
.selected_nodes
.selected_layers(&self.metadata)
.any(|selected_node_id| selected_node_id.to_node() == *current_stack_node_id)
{
nodes_to_move.push(*current_stack_node_id);
// Push all non layer sibling nodes directly upstream of the selected layer
loop {
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.get(0) else { break };
let next_node = self.network.nodes.get(node_id).expect("Stack node id should always be a node");
// If the next node is a layer, immediately break and leave current stack node as the non layer node
if next_node.is_layer {
break;
}
*current_stack_node_id = *node_id;
current_stack_node = next_node;
nodes_to_move.push(*current_stack_node_id);
}
}
// Get next node
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.get(0) else { break };
*current_stack_node_id = *node_id;
}
responses.add(GraphOperationMessage::MoveUpstreamSiblingsToChild {
new_parent: folder_id,
upstream_sibling_ids: nodes_to_move,
});
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![folder_id] });
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(NodeGraphMessage::SendGraph);
@ -576,74 +489,23 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let binding = self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata));
let get_last_elements = binding.iter().map(|x| x.last().expect("empty path")).collect::<Vec<_>>();
let mut run_document_graph_after = false;
// TODO: The `.collect()` is necessary to avoid borrowing issues with `self`. See if this can be avoided to improve performance.
let ordered_last_elements = self.metadata.all_layers().filter(|layer| get_last_elements.contains(&layer)).rev().collect::<Vec<_>>();
for layer_to_move in ordered_last_elements {
if !run_document_graph_after && self.network.connected_to_output(layer_to_move.to_node()) {
run_document_graph_after = true;
}
// Part 1: Disconnect layer to move and reconnect downstream node to upstream sibling if it exists.
self.disconnect_node(layer_to_move, responses);
// Part 2: Reconnect layer_to_move to new parent at insert index.
let (post_node_id, pre_node_id, post_node_input_index) = ModifyInputsContext::get_post_node_with_index(&self.network, parent.to_node(), insert_index);
// Layer_to_move should always correspond to a node.
let Some(layer_to_move_node) = self.network.nodes.get(&layer_to_move.to_node()) else {
continue;
};
// Move current layer to post node.
let post_node = self.network.nodes.get(&post_node_id).expect("Post node id should always refer to a node");
let current_position = layer_to_move_node.metadata.position;
let new_position = post_node.metadata.position;
// If moved to top of a layer stack, move to the left of the post node. The stack will be shifted down later.
// If moved within a stack, move directly on the post node. The rest of the stack will be shifted down later.
let offset_to_post_node = if insert_index == 0 {
new_position - current_position - IVec2::new(8, 0)
} else {
new_position - current_position
};
let mut modify_inputs = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
modify_inputs.shift_upstream(layer_to_move.to_node(), offset_to_post_node, true);
// Update post_node input to layer_to_move.
// TODO: Use insert layer between message
let post_node_mut = self.network.nodes.get_mut(&post_node_id).expect("Post node id should always refer to a node");
if let Some(NodeInput::Node { node_id, .. }) = post_node_mut.inputs.get_mut(post_node_input_index) {
*node_id = layer_to_move.to_node();
} else if let Some(node_input) = post_node_mut.inputs.get_mut(post_node_input_index) {
*node_input = NodeInput::node(layer_to_move.to_node(), 0);
}
let Some(layer_to_move_node_mut) = self.network.nodes.get_mut(&layer_to_move.to_node()) else {
continue;
};
if let Some(pre_node_id) = pre_node_id {
// If pre node exists, connect layer_to_move sibling input to that node.
if let Some(node_input) = layer_to_move_node_mut.inputs.get_mut(0) {
*node_input = NodeInput::node(pre_node_id, 0);
}
}
// shift stack down, starting at the moved node.
let mut modify_inputs: ModifyInputsContext = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
let shift = IVec2::new(0, 3);
modify_inputs.shift_upstream(layer_to_move.to_node(), shift, true);
self.metadata.load_structure(&self.network, &mut self.selected_nodes);
// Disconnect layer to move and reconnect downstream node to upstream sibling if it exists.
responses.add(NodeGraphMessage::DisconnectLayerFromStack {
node_id: layer_to_move.to_node(),
reconnect_to_sibling: true,
});
// Reconnect layer_to_move to new parent at insert index.
responses.add(GraphOperationMessage::InsertLayerAtStackIndex {
layer_id: layer_to_move.to_node(),
parent: parent.to_node(),
insert_index,
});
}
if run_document_graph_after {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(NodeGraphMessage::SendGraph);
}
DocumentMessage::NudgeSelectedLayers {
@ -736,7 +598,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let image_frame = ImageFrame { image, ..Default::default() };
use crate::messages::tool::common_functionality::graph_modification_utils;
let layer = graph_modification_utils::new_image_layer(image_frame, NodeId(generate_uuid()), self.new_layer_parent(), responses);
let layer = graph_modification_utils::new_image_layer(image_frame, NodeId(generate_uuid()), self.new_layer_parent(true), responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
@ -754,7 +616,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
use crate::messages::tool::common_functionality::graph_modification_utils;
let viewport_location = mouse.map_or(ipp.viewport_bounds.center() + ipp.viewport_bounds.top_left, |pos| pos.into());
let center_in_viewport = DAffine2::from_translation(self.metadata().document_to_viewport.inverse().transform_point2(viewport_location - ipp.viewport_bounds.top_left));
let layer = graph_modification_utils::new_svg_layer(svg, center_in_viewport, NodeId(generate_uuid()), self.new_layer_parent(), responses);
let layer = graph_modification_utils::new_svg_layer(svg, center_in_viewport, NodeId(generate_uuid()), self.new_layer_parent(true), responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
}
@ -994,14 +856,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
DocumentMessage::UngroupSelectedLayers => {
responses.add(DocumentMessage::StartTransaction);
let mut run_document_graph_after = false;
let folder_paths = self.metadata().folders_sorted_by_most_nested(self.selected_nodes.selected_layers(self.metadata()));
for folder in folder_paths {
if !run_document_graph_after && self.network.connected_to_output(folder.to_node()) {
run_document_graph_after = true;
}
// Cannot ungroup artboard
let folder_node = self.network.nodes.get(&folder.to_node()).expect("Folder node should always exist");
if folder_node.is_artboard() {
@ -1017,30 +873,29 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
// Move child_layer stack x position to folder stack
let child_layer_node = self.network.nodes.get(&child_layer_node_id).expect("Child node should always exist for layer");
let offset = folder_node.metadata.position - child_layer_node.metadata.position;
let mut modify_inputs = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
modify_inputs.shift_upstream(child_layer_node_id, offset, true);
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: child_layer_node_id,
shift: offset,
shift_self: true,
});
// Set the input for the node downstream of folder to the first layer node
// Set the primary input for the node downstream of folder to the first layer node
let Some((downstream_node_id, downstream_input_index)) = DocumentMessageHandler::get_downstream_node(&self.network, &self.metadata, folder) else {
log::error!("Downstream node should always exist when moving layer");
continue;
};
let Some(NodeInput::Node { node_id, .. }) = self
.network
.nodes
.get_mut(&downstream_node_id)
.expect("downstream node should always exist")
.inputs
.get_mut(downstream_input_index)
else {
log::error!("Could not get downstream node input");
continue;
};
*node_id = child_layer_node_id;
// Output_index must be 0 since layers only have 1 output
let downstream_input = NodeInput::node(child_layer_node_id, 0);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: downstream_node_id,
input_index: downstream_input_index,
input: downstream_input,
});
// Get the node that feeds into the primary input for the folder (if it exists)
if let Some(NodeInput::Node { node_id, .. }) = self.network.nodes.get(&folder.to_node()).expect("Folder should always exist").inputs.get(0) {
let upstream_sibling_id = *node_id;
let layer_upstream_sibling_id = *node_id;
// Get the node at the bottom of the first layer node stack
let mut last_child_node_id = child_layer_node_id;
@ -1051,12 +906,13 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
last_child_node_id = *node_id;
}
// Connect the primary input of the bottom layer to the node to the upstream sibling
let Some(node_input) = self.network.nodes.get_mut(&last_child_node_id).expect("Last child node should always exist").inputs.get_mut(0) else {
log::error!("Could not get last child node primary input");
continue;
};
*node_input = NodeInput::node(upstream_sibling_id, 0);
// Connect the primary input of the bottom layer of the node to the upstream sibling
let bottom_layer_node_input = NodeInput::node(layer_upstream_sibling_id, 0);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: last_child_node_id,
input_index: 0,
input: bottom_layer_node_input,
});
// Shift upstream_sibling down by the height of the child layer stack
let top_of_stack = self.network.nodes.get(&child_layer_node_id).expect("Child layer should always exist for child layer id");
@ -1064,24 +920,25 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let target_distance = bottom_of_stack.metadata.position.y - top_of_stack.metadata.position.y;
let folder_node = self.network.nodes.get(&folder.to_node()).expect("Folder node should always exist");
let upstream_sibling_node = self.network.nodes.get(&upstream_sibling_id).expect("Upstream sibling node should always exist");
let upstream_sibling_node = self.network.nodes.get(&layer_upstream_sibling_id).expect("Upstream sibling node should always exist");
let current_distance = upstream_sibling_node.metadata.position.y - folder_node.metadata.position.y;
let y_offset = target_distance - current_distance + 3;
let mut modify_inputs = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
modify_inputs.shift_upstream(upstream_sibling_id, IVec2::new(0, y_offset), true);
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: layer_upstream_sibling_id,
shift: IVec2::new(0, y_offset),
shift_self: true,
});
}
// Delete folder and all horizontal inputs
// Delete folder and all horizontal inputs, also deletes node in metadata
responses.add(NodeGraphMessage::DeleteNodes {
node_ids: vec![folder.to_node()],
reconnect: true,
});
}
if run_document_graph_after {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(NodeGraphMessage::SendGraph);
}
@ -1385,23 +1242,6 @@ impl DocumentMessageHandler {
self.saved_hash = None;
}
}
// TODO: Replace with disconnect message
pub fn disconnect_input(layer_to_disconnect_node: &mut DocumentNode, input_index: usize) {
let Some(node_type) = resolve_document_node_type(&layer_to_disconnect_node.name) else {
warn!("Node {} not in library", layer_to_disconnect_node.name);
return;
};
let Some(existing_input) = layer_to_disconnect_node.inputs.get_mut(input_index) else {
warn!("Node does not have and input at the selected index");
return;
};
let mut default_input = node_type.inputs[input_index].default.clone();
if let NodeInput::Value { exposed, .. } = &mut default_input {
*exposed = existing_input.is_exposed();
}
*existing_input = default_input;
}
pub fn get_downstream_node(network: &NodeNetwork, metadata: &DocumentMetadata, layer_to_move: LayerNodeIdentifier) -> Option<(NodeId, usize)> {
let mut downstream_layer = None;
@ -1442,35 +1282,6 @@ impl DocumentMessageHandler {
})
}
// TODO: move into message
pub fn disconnect_node(&mut self, layer_to_disconnect: LayerNodeIdentifier, responses: &mut VecDeque<Message>) {
let Some((downstream_node_id, downstream_input_index)) = DocumentMessageHandler::get_downstream_node(&self.network, &self.metadata, layer_to_disconnect) else {
log::error!("Downstream node should always exist when moving layer");
return;
};
let layer_to_move_sibling_input = self.network.nodes.get(&layer_to_disconnect.to_node()).and_then(|node| node.inputs.get(0));
if let Some(NodeInput::Node { node_id, .. }) = layer_to_move_sibling_input {
let upstream_sibling_id = node_id.clone();
let Some(downstream_node) = self.network.nodes.get_mut(&downstream_node_id) else { return };
if let Some(NodeInput::Node { node_id, .. }) = downstream_node.inputs.get_mut(downstream_input_index) {
*node_id = upstream_sibling_id;
}
let upstream_shift = IVec2::new(0, -3);
let mut modify_inputs = ModifyInputsContext::new(&mut self.network, &mut self.metadata, &mut self.node_graph_handler, responses);
modify_inputs.shift_upstream(upstream_sibling_id, upstream_shift, true);
} else {
// Disconnect node directly downstream if upstream sibling doesn't exist
let Some(downstream_node) = self.network.nodes.get_mut(&downstream_node_id) else { return };
DocumentMessageHandler::disconnect_input(downstream_node, downstream_input_index);
}
let Some(to_move) = self.network.nodes.get_mut(&layer_to_disconnect.to_node()) else { return };
DocumentMessageHandler::disconnect_input(to_move, 0);
}
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
///
/// This function updates the insert index so that it points to the same place after the specified `layers` are deleted.
@ -1481,12 +1292,32 @@ impl DocumentMessageHandler {
}
/// Finds the parent folder which, based on the current selections, should be the container of any newly added layers.
pub fn new_layer_parent(&self) -> LayerNodeIdentifier {
pub fn new_layer_parent(&self, include_self: bool) -> LayerNodeIdentifier {
self.metadata()
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), true)
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), include_self)
.unwrap_or_else(|| self.metadata().active_artboard())
}
fn get_calculated_insert_index(&self, parent: LayerNodeIdentifier) -> isize {
parent
.children(self.metadata())
.enumerate()
.find_map(|(index, direct_child)| {
if self.selected_nodes.selected_layers(self.metadata()).any(|selected| selected == direct_child) {
return Some(index as isize);
}
for descendant in direct_child.descendants(self.metadata()) {
if self.selected_nodes.selected_layers(self.metadata()).any(|selected| selected == descendant) {
return Some(index as isize);
}
}
None
})
.unwrap_or(-1)
}
/// Loads layer resources such as creating the blob URLs for the images and loading all of the fonts in the document.
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>) {
let mut fonts = HashSet::new();

View File

@ -19,10 +19,36 @@ use glam::{DAffine2, DVec2, IVec2};
#[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GraphOperationMessage {
AddNodesAsChild {
nodes: HashMap<NodeId, DocumentNode>,
parent: LayerNodeIdentifier,
insert_index: isize,
},
DisconnectInput {
node_id: NodeId,
input_index: usize,
},
FillSet {
layer: LayerNodeIdentifier,
fill: Fill,
},
InsertLayerAtStackIndex {
layer_id: NodeId,
parent: NodeId,
insert_index: usize,
},
InsertNodeBetween {
post_node_id: NodeId,
post_node_input_index: usize,
insert_node_output_index: usize,
insert_node_id: NodeId,
insert_node_input_index: usize,
pre_node_output_index: usize,
pre_node_id: NodeId,
},
MoveSelectedSiblingsToChild {
new_parent: NodeId,
},
OpacitySet {
layer: LayerNodeIdentifier,
opacity: f64,
@ -64,10 +90,6 @@ pub enum GraphOperationMessage {
layer: LayerNodeIdentifier,
strokes: Vec<BrushStroke>,
},
MoveUpstreamSiblingsToChild {
new_parent: NodeId,
upstream_sibling_ids: Vec<NodeId>,
},
NewArtboard {
id: NodeId,
artboard: Artboard,

View File

@ -1,5 +1,6 @@
use super::transform_utils::{self, LayerBounds};
use super::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_types::resolve_document_node_type;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes};
use crate::messages::prelude::*;
@ -36,11 +37,185 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
} = data;
match message {
GraphOperationMessage::AddNodesAsChild { nodes, parent, insert_index } => {
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, NodeId(generate_uuid()))).collect();
let shift = nodes
.get(&NodeId(0))
.and_then(|node| {
document_network
.nodes
.get(&parent.to_node())
.map(|layer| layer.metadata.position - node.metadata.position + IVec2::new(-8, 0))
})
.unwrap_or_default();
for (old_id, mut document_node) in nodes {
// Shift copied node
document_node.metadata.position += shift;
// Get the new, non-conflicting id
let node_id = *new_ids.get(&old_id).unwrap();
document_node = document_node.map_ids(NodeGraphMessageHandler::default_node_input, &new_ids);
// Insert node into network
document_network.nodes.insert(node_id, document_node);
}
let Some(new_layer_id) = new_ids.get(&NodeId(0)) else {
log::error!("Could not get layer node when adding as child");
return;
};
let insert_index = if insert_index < 0 { 0 } else { insert_index as usize };
let (downstream_node, upstream_node, input_index) = ModifyInputsContext::get_post_node_with_index(document_network, parent.to_node(), insert_index);
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![*new_layer_id] });
if let Some(upstream_node) = upstream_node {
responses.add(GraphOperationMessage::InsertNodeBetween {
post_node_id: downstream_node,
post_node_input_index: input_index,
insert_node_output_index: 0,
insert_node_id: *new_layer_id,
insert_node_input_index: 0,
pre_node_output_index: 0,
pre_node_id: upstream_node,
})
} else {
responses.add(NodeGraphMessage::SetNodeInput {
node_id: downstream_node,
input_index: input_index,
input: NodeInput::node(*new_layer_id, 0),
})
}
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: *new_layer_id,
shift: IVec2::new(0, 3),
shift_self: true,
});
responses.add(NodeGraphMessage::RunDocumentGraph);
}
GraphOperationMessage::DisconnectInput { node_id, input_index } => {
let Some(node_to_disconnect) = document_network.nodes.get(&node_id) else {
warn!("Node {} not found in DisconnectInput", node_id);
return;
};
let Some(node_type) = resolve_document_node_type(&node_to_disconnect.name) else {
warn!("Node {} not in library", node_to_disconnect.name);
return;
};
let Some(existing_input) = node_to_disconnect.inputs.get(input_index) else {
warn!("Node does not have an input at the selected index");
return;
};
let mut input = node_type.inputs[input_index].default.clone();
if let NodeInput::Value { exposed, .. } = &mut input {
*exposed = existing_input.is_exposed();
}
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
}
GraphOperationMessage::FillSet { layer, fill } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.fill_set(fill);
}
}
GraphOperationMessage::InsertLayerAtStackIndex { layer_id, parent, insert_index } => {
let (post_node_id, pre_node_id, post_node_input_index) = ModifyInputsContext::get_post_node_with_index(&document_network, parent, insert_index);
// `layer_to_move` should always correspond to a node.
let Some(layer_to_move_node) = document_network.nodes.get(&layer_id) else {
log::error!("Layer node not found when inserting node {} at index {}", layer_id, insert_index);
return;
};
// Move current layer to post node.
let post_node = document_network.nodes.get(&post_node_id).expect("Post node id should always refer to a node");
let current_position = layer_to_move_node.metadata.position;
let new_position = post_node.metadata.position;
// If moved to top of a layer stack, move to the left of the post node. If moved within a stack, move directly on the post node. The stack will be shifted down later.
let offset_to_post_node = if insert_index == 0 {
new_position - current_position - IVec2::new(8, 0)
} else {
new_position - current_position
};
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: layer_id,
shift: offset_to_post_node,
shift_self: true,
});
// Update post_node input to layer_to_move.
if let Some(upstream_node) = pre_node_id {
responses.add(GraphOperationMessage::InsertNodeBetween {
post_node_id: post_node_id,
post_node_input_index: post_node_input_index,
insert_node_output_index: 0,
insert_node_id: layer_id,
insert_node_input_index: 0,
pre_node_output_index: 0,
pre_node_id: upstream_node,
})
} else {
responses.add(NodeGraphMessage::SetNodeInput {
node_id: post_node_id,
input_index: post_node_input_index,
input: NodeInput::node(layer_id, 0),
})
}
// Shift stack down, starting at the moved node.
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: layer_id,
shift: IVec2::new(0, 3),
shift_self: true,
});
}
GraphOperationMessage::InsertNodeBetween {
post_node_id,
post_node_input_index,
insert_node_output_index,
insert_node_id,
insert_node_input_index,
pre_node_output_index,
pre_node_id,
} => {
let Some(post_node) = document_network.nodes.get(&post_node_id) else {
error!("Post node not found");
return;
};
let Some((post_node_input_index, _)) = post_node.inputs.iter().enumerate().filter(|input| input.1.is_exposed()).nth(post_node_input_index) else {
error!("Failed to find input index {post_node_input_index} on node {post_node_id:#?}");
return;
};
let Some(insert_node) = document_network.nodes.get(&insert_node_id) else {
error!("Insert node not found");
return;
};
let Some((insert_node_input_index, _)) = insert_node.inputs.iter().enumerate().filter(|input| input.1.is_exposed()).nth(insert_node_input_index) else {
error!("Failed to find input index {insert_node_input_index} on node {insert_node_id:#?}");
return;
};
let post_input = NodeInput::node(insert_node_id, insert_node_output_index);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: post_node_id,
input_index: post_node_input_index,
input: post_input,
});
let insert_input = NodeInput::node(pre_node_id, pre_node_output_index);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: insert_node_id,
input_index: insert_node_input_index,
input: insert_input,
});
}
GraphOperationMessage::OpacitySet { layer, opacity } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.opacity_set(opacity);
@ -103,67 +278,78 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
modify_inputs.brush_modify(strokes);
}
}
GraphOperationMessage::MoveUpstreamSiblingsToChild { new_parent, upstream_sibling_ids } => {
// Start with the furthest upstream node, move it as a child of the new folder, and continue downstream for each layer in vec
for node_to_move in upstream_sibling_ids.iter().rev() {
// Connect pre node to post node, or disconnect pre node if post node doesn't exist
let mut pre_node_id = new_parent;
loop {
let Some(NodeInput::Node { node_id, .. }) = document_network.nodes.get(&pre_node_id).and_then(|node| node.inputs.get(0)) else {
log::error!("End of stack should never be reached");
return;
};
if *node_id == *node_to_move {
break;
}
pre_node_id = *node_id;
}
if let Some(NodeInput::Node { node_id, .. }) = document_network.nodes.get(&node_to_move).and_then(|node| node.inputs.get(0)) {
let post_node_id = *node_id;
let Some(NodeInput::Node { node_id, .. }) = document_network.nodes.get_mut(&pre_node_id).and_then(|node| node.inputs.get_mut(0)) else {
log::error!("Pre node should always have primary input");
return;
};
*node_id = post_node_id;
} else {
DocumentMessageHandler::disconnect_input(document_network.nodes.get_mut(&pre_node_id).expect("Upstream sibling should always exist"), 0);
}
// Connect upstream sibling to the secondary input of the parent
let Some(parent_secondary_input) = document_network.nodes.get(&new_parent).and_then(|node| node.inputs.get(1)) else {
log::error!("Could not get child node input for current node");
return;
};
// Insert upstream_sibling_node at top of group stack
if let NodeInput::Node { node_id, .. } = parent_secondary_input {
// If there is already a node at the top of the stack, insert upstream_sibling_node in between
let current_child = *node_id;
let Some(upstream_sibling_input) = document_network.nodes.get_mut(&node_to_move).and_then(|node| node.inputs.get_mut(0)) else {
log::error!("Could not get upstream sibling node input");
return;
};
*upstream_sibling_input = NodeInput::node(current_child, 0);
}
let Some(parent_secondary_input_mut) = document_network.nodes.get_mut(&new_parent).and_then(|node| node.inputs.get_mut(1)) else {
log::error!("Could not get child node input for current node");
return;
};
*parent_secondary_input_mut = NodeInput::node(*node_to_move, 0);
}
let Some(most_upstream_sibling) = upstream_sibling_ids.last() else {
GraphOperationMessage::MoveSelectedSiblingsToChild { new_parent } => {
let group_layer = LayerNodeIdentifier::new(new_parent, &document_network);
let Some(group_parent) = group_layer.parent(&document_metadata) else {
log::error!("Could not find parent for layer {:?}", group_layer);
return;
};
DocumentMessageHandler::disconnect_input(document_network.nodes.get_mut(&most_upstream_sibling).expect("Upstream sibling should always exist"), 0);
let top_of_stack = upstream_sibling_ids.first().expect("Upstream nodes to move cannot be empty");
let upstream_shift = IVec2::new(-8, 0);
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
modify_inputs.shift_upstream(*top_of_stack, upstream_shift, true);
// Create a vec of nodes to move with all selected layers in the parent layer child stack, as well as each non layer sibling directly upstream of the selected layer
let mut selected_siblings = Vec::new();
// Skip over horizontal non layer node chain that feeds into parent
let Some(mut current_stack_node_id) = group_parent.first_child(&document_metadata).and_then(|current_stack_node| Some(current_stack_node.to_node())) else {
log::error!("Folder should always have child");
return;
};
let current_stack_node_id = &mut current_stack_node_id;
loop {
let mut current_stack_node = document_network.nodes.get(current_stack_node_id).expect("Current stack node id should always be a node");
// Check if the current stack node is a selected layer
if selected_nodes
.selected_layers(&document_metadata)
.any(|selected_node_id| selected_node_id.to_node() == *current_stack_node_id)
{
selected_siblings.push(*current_stack_node_id);
// Push all non layer sibling nodes directly upstream of the selected layer
loop {
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.get(0) else { break };
let next_node = document_network.nodes.get(node_id).expect("Stack node id should always be a node");
// If the next node is a layer, immediately break and leave current stack node as the non layer node
if next_node.is_layer {
break;
}
*current_stack_node_id = *node_id;
current_stack_node = next_node;
selected_siblings.push(*current_stack_node_id);
}
}
// Get next node
let Some(NodeInput::Node { node_id, .. }) = current_stack_node.inputs.get(0) else { break };
*current_stack_node_id = *node_id;
}
// Start with the furthest upstream node, move it as a child of the new folder, and continue downstream for each layer in vec
for node_to_move in selected_siblings.iter().rev() {
// Connect downstream node to upstream node, or disconnect downstream node if upstream node doesn't exist
responses.add(NodeGraphMessage::DisconnectLayerFromStack {
node_id: *node_to_move,
reconnect_to_sibling: true,
});
responses.add(GraphOperationMessage::InsertLayerAtStackIndex {
layer_id: *node_to_move,
parent: new_parent,
insert_index: 0,
});
}
let Some(most_upstream_sibling) = selected_siblings.last() else {
return;
};
responses.add(GraphOperationMessage::DisconnectInput {
node_id: *most_upstream_sibling,
input_index: 0,
});
}
GraphOperationMessage::NewArtboard { id, artboard } => {
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);

View File

@ -1,5 +1,6 @@
use crate::messages::prelude::*;
use glam::IVec2;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
use graph_craft::proto::GraphErrors;
@ -36,6 +37,10 @@ pub enum NodeGraphMessage {
node_id: NodeId,
input_index: usize,
},
DisconnectLayerFromStack {
node_id: NodeId,
reconnect_to_sibling: bool,
},
EnterNestedNetwork {
node: NodeId,
},
@ -92,6 +97,10 @@ pub enum NodeGraphMessage {
input_index: usize,
input: NodeInput,
},
SetNodePosition {
node_id: NodeId,
position: IVec2,
},
SetQualifiedInputValue {
node_path: Vec<NodeId>,
input_index: usize,
@ -101,6 +110,11 @@ pub enum NodeGraphMessage {
ShiftNode {
node_id: NodeId,
},
ShiftUpstream {
node_id: NodeId,
shift: IVec2,
shift_self: bool,
},
SetVisibility {
node_id: NodeId,
visible: bool,

View File

@ -9,6 +9,7 @@ use crate::application::generate_uuid;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::graph_operation::load_network_structure;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::node_graph::document_node_types::{resolve_document_node_type, DocumentInputType, NodePropertiesContext};
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry, SelectedNodes};
@ -161,15 +162,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
let outward_links = document_network.collect_outwards_links();
for (_, upstream_id) in document_network.upstream_flow_back_from_nodes(vec![*child_id], graph_craft::document::FlowType::UpstreamFlow) {
// TODO: move into a document_network function .is_sole_dependent. This function does a downstream traversal starting from the current node,
// TODO: and only traverses for nodes that are not in the delete_nodes set. If all downstream nodes converge to some node in the delete_nodes set,
// TODO: then it is a sole dependent. If the output node is eventually reached, then it is not a sole dependent. This means disconnected branches
// TODO: that do not feed into the delete_nodes set or the output node will be deleted.
// This does a downstream traversal starting from the current node, and ending at either a node in the delete_nodes set or the output.
// If the traversal find as child node of a node in the delete_nodes set, then it is a sole dependent. If the output node is eventually reached, then it is not a sole dependent.
let mut stack = vec![upstream_id];
let mut can_delete = true;
// TODO: Add iteration limit to force break in case of infinite while loop
while let Some(current_node) = stack.pop() {
if let Some(downstream_nodes) = outward_links.get(&current_node) {
for downstream_node in downstream_nodes {
@ -238,35 +235,65 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
});
}
NodeGraphMessage::DisconnectNodes { node_id, input_index } => {
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::DisconnectInput { node_id, input_index });
let Some(network) = document_network.nested_network(&self.network) else {
warn!("No network");
return;
};
let Some(node) = network.nodes.get(&node_id) else {
warn!("Invalid node");
return;
};
let Some(node_type) = resolve_document_node_type(&node.name) else {
warn!("Node {} not in library", node.name);
return;
};
let Some((input_index, existing_input)) = node.inputs.iter().enumerate().filter(|(_, input)| input.is_exposed()).nth(input_index) else {
return;
};
let mut input = node_type.inputs[input_index].default.clone();
if let NodeInput::Value { exposed, .. } = &mut input {
*exposed = existing_input.is_exposed();
}
responses.add(DocumentMessage::StartTransaction);
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
if network.connected_to_output(node_id) {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::SendGraph);
}
NodeGraphMessage::DisconnectLayerFromStack { node_id, reconnect_to_sibling } => {
let Some(network) = document_network.nested_network(&self.network) else {
warn!("No network");
return;
};
// Ensure node is a layer and create LayerNodeIdentifier
if network.nodes.get(&node_id).is_some_and(|node| !node.is_layer) {
log::error!("Non layer node passed to DisconnectLayer");
return;
}
let layer_to_disconnect = LayerNodeIdentifier::new(node_id, &network);
let Some((downstream_node_id, downstream_input_index)) = DocumentMessageHandler::get_downstream_node(&network, &document_metadata, layer_to_disconnect) else {
log::error!("Downstream node should always exist when moving layer");
return;
};
let layer_to_move_sibling_input = network.nodes.get(&layer_to_disconnect.to_node()).and_then(|node| node.inputs.get(0));
if let Some(NodeInput::Node { node_id, .. }) = layer_to_move_sibling_input.and_then(|node_input| if reconnect_to_sibling { Some(node_input) } else { None }) {
let upstream_sibling_id = *node_id;
let Some(downstream_node) = document_network.nodes.get_mut(&downstream_node_id) else { return };
if let Some(NodeInput::Node { node_id, .. }) = downstream_node.inputs.get_mut(downstream_input_index) {
*node_id = upstream_sibling_id;
}
let upstream_shift = IVec2::new(0, -3);
responses.add(NodeGraphMessage::ShiftUpstream {
node_id: upstream_sibling_id,
shift: upstream_shift,
shift_self: true,
});
} else {
// Disconnect node directly downstream if upstream sibling doesn't exist
responses.add(GraphOperationMessage::DisconnectInput {
node_id: downstream_node_id,
input_index: downstream_input_index,
});
}
responses.add(GraphOperationMessage::DisconnectInput {
node_id: layer_to_disconnect.to_node(),
input_index: 0,
});
}
NodeGraphMessage::EnterNestedNetwork { node } => {
if let Some(network) = document_network.nested_network(&self.network) {
if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() {
@ -370,37 +397,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
error!("No network");
return;
};
let Some(post_node) = network.nodes.get(&post_node_id) else {
error!("Post node not found");
return;
};
let Some((post_node_input_index, _)) = post_node.inputs.iter().enumerate().filter(|input| input.1.is_exposed()).nth(post_node_input_index) else {
error!("Failed to find input index {post_node_input_index} on node {post_node_id:#?}");
return;
};
let Some(insert_node) = network.nodes.get(&insert_node_id) else {
error!("Insert node not found");
return;
};
let Some((insert_node_input_index, _)) = insert_node.inputs.iter().enumerate().filter(|input| input.1.is_exposed()).nth(insert_node_input_index) else {
error!("Failed to find input index {insert_node_input_index} on node {insert_node_id:#?}");
return;
};
responses.add(DocumentMessage::StartTransaction);
let post_input = NodeInput::node(insert_node_id, insert_node_output_index);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: post_node_id,
input_index: post_node_input_index,
input: post_input,
});
let insert_input = NodeInput::node(pre_node_id, pre_node_output_index);
responses.add(NodeGraphMessage::SetNodeInput {
node_id: insert_node_id,
input_index: insert_node_input_index,
input: insert_input,
responses.add(GraphOperationMessage::InsertNodeBetween {
post_node_id,
post_node_input_index,
insert_node_output_index,
insert_node_id,
insert_node_input_index,
pre_node_output_index,
pre_node_id,
});
if network.connected_to_output(insert_node_id) {
@ -520,6 +526,26 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
NodeGraphMessage::SetNodePosition { node_id, position } => {
let Some(network) = document_network.nested_network_mut(&self.network) else {
warn!("No network");
return;
};
let Some(node) = network.nodes.get_mut(&node_id) else {
log::error!("Failed to find node {node_id} when setting position");
return;
};
node.metadata.position = position;
// Since document structure doesn't change, just update the nodes
if graph_view_overlay_open {
let links = Self::collect_links(network);
let nodes = self.collect_nodes(&links, network);
responses.add(FrontendMessage::UpdateNodeGraph { nodes, links });
}
}
NodeGraphMessage::SetQualifiedInputValue { node_path, input_index, value } => {
let Some((node_id, node_path)) = node_path.split_last() else {
error!("Node path is empty");
@ -591,6 +617,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
self.send_graph(network, graph_view_overlay_open, document_metadata, selected_nodes, collapsed, responses);
}
NodeGraphMessage::ShiftUpstream { node_id, shift, shift_self } => {
let Some(network) = document_network.nested_network_mut(&self.network) else {
warn!("No network");
return;
};
let mut modify_inputs = ModifyInputsContext::new(network, document_metadata, self, responses);
modify_inputs.shift_upstream(node_id, shift, shift_self);
}
NodeGraphMessage::ToggleSelectedVisibility => {
responses.add(DocumentMessage::StartTransaction);

View File

@ -183,30 +183,29 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
let ordered_last_elements: Vec<_> = active_document.metadata.all_layers().filter(|layer| get_last_elements.contains(&layer)).collect();
for layer in ordered_last_elements {
let node = layer.to_node();
let previous_alias = active_document.network().nodes.get(&node).map(|node| node.alias.clone()).unwrap_or_default();
let layer_node_id = layer.to_node();
let previous_alias = active_document.network().nodes.get(&layer_node_id).map(|node| node.alias.clone()).unwrap_or_default();
let Some(node) = active_document
let mut copy_ids = HashMap::new();
copy_ids.insert(layer_node_id, NodeId(0 as u64));
if let Some(input_node) = active_document
.network()
.nodes
.get(&node)
.get(&layer_node_id)
.and_then(|node| if node.is_layer { node.inputs.get(1) } else { node.inputs.get(0) })
.and_then(|input| input.as_node())
else {
continue;
{
active_document
.network()
.upstream_flow_back_from_nodes(vec![input_node], graph_craft::document::FlowType::UpstreamFlow)
.enumerate()
.for_each(|(index, (_, node_id))| {
copy_ids.insert(node_id, NodeId((index + 1) as u64));
});
};
buffer.push(CopyBufferEntry {
nodes: NodeGraphMessageHandler::copy_nodes(
active_document.network(),
&active_document
.network()
.upstream_flow_back_from_nodes(vec![node], graph_craft::document::FlowType::UpstreamFlow)
.enumerate()
.map(|(index, (_, node_id))| (node_id, NodeId(index as u64)))
.collect(),
)
.collect(),
nodes: NodeGraphMessageHandler::copy_nodes(active_document.network(), &copy_ids).collect(),
selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()),
visible: active_document.selected_nodes.layer_visible(layer, active_document.metadata()),
locked: active_document.selected_nodes.layer_locked(layer, active_document.metadata()),
@ -384,26 +383,17 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| {
if self.active_document().is_some() {
trace!("Pasting into folder {parent:?} as index: {insert_index}");
let id = NodeId(generate_uuid());
responses.add(GraphOperationMessage::NewCustomLayer {
id,
nodes: entry.nodes.clone(),
responses.add(GraphOperationMessage::AddNodesAsChild {
nodes: entry.clone().nodes,
parent,
insert_index,
alias: entry.alias.clone(),
});
if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
if !entry.visible {
responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
}
if entry.locked {
responses.add(NodeGraphMessage::SetLocked { node_id: id, locked: true });
}
}
};
responses.add(DocumentMessage::DeselectAllLayers);
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
paste(entry, responses)
}
@ -411,30 +401,19 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
PortfolioMessage::PasteSerializedData { data } => {
if let Some(document) = self.active_document() {
if let Ok(data) = serde_json::from_str::<Vec<CopyBufferEntry>>(&data) {
let parent = document.new_layer_parent();
let parent = document.new_layer_parent(false);
responses.add(DocumentMessage::DeselectAllLayers);
responses.add(DocumentMessage::StartTransaction);
for entry in data.into_iter().rev() {
document.load_layer_resources(responses);
let id = NodeId(generate_uuid());
responses.add(GraphOperationMessage::NewCustomLayer {
id,
responses.add(GraphOperationMessage::AddNodesAsChild {
nodes: entry.nodes,
parent,
insert_index: -1,
alias: entry.alias,
});
if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
if !entry.visible {
responses.add(NodeGraphMessage::SetVisibility { node_id: id, visible: false });
}
}
responses.add(DocumentMessage::CommitTransaction);
}
}
}

View File

@ -420,7 +420,7 @@ fn new_brush_layer(document: &DocumentMessageHandler, responses: &mut VecDeque<M
responses.add(GraphOperationMessage::NewCustomLayer {
id,
nodes: HashMap::from([(NodeId(0), brush_node)]),
parent: document.new_layer_parent(),
parent: document.new_layer_parent(true),
insert_index: -1,
alias: String::new(),
});

View File

@ -202,7 +202,7 @@ impl Fsm for EllipseToolFsmState {
// Create a new ellipse vector shape
let subpath = bezier_rs::Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE);
let manipulator_groups = subpath.manipulator_groups().to_vec();
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(), responses);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(true), responses);
graph_modification_utils::set_manipulator_colinear_handles_state(&manipulator_groups, layer, true, responses);
shape_data.layer = Some(layer);

View File

@ -230,7 +230,7 @@ impl Fsm for FreehandToolFsmState {
} else {
responses.add(DocumentMessage::DeselectAllLayers);
let parent = document.new_layer_parent();
let parent = document.new_layer_parent(true);
let transform = document.metadata().transform_to_viewport(parent);
let pos = transform.inverse().transform_point2(input.mouse.position);
let subpath = bezier_rs::Subpath::from_anchors([pos], false);

View File

@ -181,7 +181,7 @@ impl Fsm for LineToolFsmState {
responses.add(DocumentMessage::StartTransaction);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(), responses);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(true), responses);
responses.add(GraphOperationMessage::StrokeSet {
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),

View File

@ -232,7 +232,7 @@ impl PenToolData {
input: &InputPreprocessorMessageHandler,
responses: &mut VecDeque<Message>,
) {
let parent = document.new_layer_parent();
let parent = document.new_layer_parent(true);
// Deselect layers because we are now creating a new layer
responses.add(DocumentMessage::DeselectAllLayers);

View File

@ -247,7 +247,7 @@ impl Fsm for PolygonToolFsmState {
PolygonType::Convex => bezier_rs::Subpath::new_regular_polygon(DVec2::ZERO, tool_options.vertices as u64, 1.),
PolygonType::Star => bezier_rs::Subpath::new_star_polygon(DVec2::ZERO, tool_options.vertices as u64, 1., 0.5),
};
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(), responses);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(true), responses);
polygon_data.layer = Some(layer);
let fill_color = tool_options.fill.active_color();

View File

@ -209,7 +209,7 @@ impl Fsm for RectangleToolFsmState {
responses.add(DocumentMessage::StartTransaction);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(), responses);
let layer = graph_modification_utils::new_vector_layer(vec![subpath], NodeId(generate_uuid()), document.new_layer_parent(true), responses);
shape_data.layer = Some(layer);
let fill_color = tool_options.fill.active_color();

View File

@ -208,7 +208,7 @@ impl Fsm for SplineToolFsmState {
responses.add(DocumentMessage::StartTransaction);
responses.add(DocumentMessage::DeselectAllLayers);
let parent = document.new_layer_parent();
let parent = document.new_layer_parent(true);
let transform = document.metadata().transform_to_viewport(parent);
//tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);

View File

@ -283,7 +283,7 @@ impl TextToolData {
text: String::new(),
font: editing_text.font.clone(),
size: editing_text.font_size,
parent: document.new_layer_parent(),
parent: document.new_layer_parent(true),
insert_index: -1,
});
responses.add(GraphOperationMessage::FillSet {