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:
parent
8d83fa7079
commit
4c3856833b
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(¤t_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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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(), ©_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue