mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-03 01:08:12 +08:00
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:
co-authored by
Keavon Chambers
parent
8d83fa7079
commit
4c3856833b
@@ -1,3 +1,4 @@
|
|||||||
|
use super::utility_types::clipboards::Clipboard;
|
||||||
use super::utility_types::error::EditorError;
|
use super::utility_types::error::EditorError;
|
||||||
use super::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, OptionBoundsSnapping, OptionPointSnapping, SnappingOptions, SnappingState};
|
use super::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, OptionBoundsSnapping, OptionPointSnapping, SnappingOptions, SnappingState};
|
||||||
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
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::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::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
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::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::node_graph::NodeGraphHandlerData;
|
||||||
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
|
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
|
||||||
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
|
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 });
|
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
|
||||||
}
|
}
|
||||||
DocumentMessage::DuplicateSelectedLayers => {
|
DocumentMessage::DuplicateSelectedLayers => {
|
||||||
self.backup(responses);
|
let parent = self.new_layer_parent(false);
|
||||||
for layer_ancestors in self.metadata.shallowest_unique_layers(self.selected_nodes.selected_layers(&self.metadata)) {
|
let calculated_insert_index = self.get_calculated_insert_index(parent);
|
||||||
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 nodes = NodeGraphMessageHandler::copy_nodes(
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
self.network(),
|
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
|
||||||
&self
|
responses.add(PortfolioMessage::PasteIntoFolder {
|
||||||
.network()
|
clipboard: Clipboard::Internal,
|
||||||
.upstream_flow_back_from_nodes(vec![node], FlowType::UpstreamFlow)
|
parent,
|
||||||
.enumerate()
|
insert_index: calculated_insert_index,
|
||||||
.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(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
DocumentMessage::FlipSelectedLayers { flip_axis } => {
|
DocumentMessage::FlipSelectedLayers { flip_axis } => {
|
||||||
self.backup(responses);
|
self.backup(responses);
|
||||||
@@ -356,6 +335,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
}
|
}
|
||||||
DocumentMessage::GroupSelectedLayers => {
|
DocumentMessage::GroupSelectedLayers => {
|
||||||
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let parent = self
|
let parent = self
|
||||||
.metadata()
|
.metadata()
|
||||||
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), false)
|
.deepest_common_ancestor(self.selected_nodes.selected_layers(self.metadata()), false)
|
||||||
@@ -390,8 +371,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect above and below the old layer location
|
responses.add(NodeGraphMessage::DisconnectLayerFromStack {
|
||||||
self.disconnect_node(layer, responses);
|
node_id: layer.to_node(),
|
||||||
|
reconnect_to_sibling: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Move disconnected node to folder
|
// Move disconnected node to folder
|
||||||
let folder_position = self
|
let folder_position = self
|
||||||
@@ -401,118 +384,48 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
.expect("Current folder should always exist")
|
.expect("Current folder should always exist")
|
||||||
.metadata
|
.metadata
|
||||||
.position;
|
.position;
|
||||||
|
responses.add(NodeGraphMessage::SetNodePosition {
|
||||||
let Some(layer_to_move_node_mut) = self.network.nodes.get_mut(&layer.to_node()) else {
|
node_id: layer.to_node(),
|
||||||
return;
|
position: folder_position,
|
||||||
};
|
});
|
||||||
|
|
||||||
DocumentMessageHandler::disconnect_input(layer_to_move_node_mut, 0);
|
|
||||||
layer_to_move_node_mut.metadata.position = folder_position;
|
|
||||||
|
|
||||||
// Insert node right above the folder
|
// 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)
|
let Some((folder_downstream_node_id, folder_downstream_input_index)) = DocumentMessageHandler::get_downstream_node(&self.network, &self.metadata, first_unselected_parent_folder)
|
||||||
else {
|
else {
|
||||||
log::error!("Downstream node should always exist when inserting layer");
|
log::error!("Downstream node should always exist when inserting layer");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let downstream_input = self
|
responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||||
.network
|
post_node_id: folder_downstream_node_id,
|
||||||
.nodes
|
post_node_input_index: folder_downstream_input_index,
|
||||||
.get_mut(&folder_downstream_node_id)
|
insert_node_output_index: 0,
|
||||||
.and_then(|node| node.inputs.get_mut(folder_downstream_input_index));
|
insert_node_id: layer.to_node(),
|
||||||
let Some(NodeInput::Node { node_id, .. }) = downstream_input else {
|
insert_node_input_index: 0,
|
||||||
log::error!("Downstream node should have a node input");
|
pre_node_output_index: 0,
|
||||||
return;
|
pre_node_id: first_unselected_parent_folder.to_node(),
|
||||||
};
|
});
|
||||||
*node_id = layer.to_node();
|
|
||||||
|
|
||||||
// Connect layer primary input to parent folder
|
responses.add(NodeGraphMessage::ShiftUpstream {
|
||||||
let Some(layer_node_input) = self.network.nodes.get_mut(&layer.to_node()).and_then(|node| node.inputs.get_mut(0)) else {
|
node_id: first_unselected_parent_folder.to_node(),
|
||||||
log::error!("Layer should always have primary input");
|
shift: IVec2::new(0, 3),
|
||||||
return;
|
shift_self: true,
|
||||||
};
|
});
|
||||||
*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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let calculated_insert_index = parent.children(self.metadata()).enumerate().find_map(|(index, direct_child)| {
|
let calculated_insert_index = self.get_calculated_insert_index(parent);
|
||||||
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 folder_id = NodeId(generate_uuid());
|
let folder_id = NodeId(generate_uuid());
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
|
||||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||||
id: folder_id,
|
id: folder_id,
|
||||||
nodes: HashMap::new(),
|
nodes: HashMap::new(),
|
||||||
parent,
|
parent,
|
||||||
insert_index: calculated_insert_index.unwrap_or(-1),
|
insert_index: calculated_insert_index,
|
||||||
alias: String::new(),
|
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
|
responses.add(GraphOperationMessage::MoveSelectedSiblingsToChild { new_parent: folder_id });
|
||||||
let mut nodes_to_move = Vec::new();
|
|
||||||
|
|
||||||
// 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::SelectedNodesSet { nodes: vec![folder_id] });
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
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 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 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.
|
// 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<_>>();
|
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 {
|
for layer_to_move in ordered_last_elements {
|
||||||
if !run_document_graph_after && self.network.connected_to_output(layer_to_move.to_node()) {
|
// Disconnect layer to move and reconnect downstream node to upstream sibling if it exists.
|
||||||
run_document_graph_after = true;
|
responses.add(NodeGraphMessage::DisconnectLayerFromStack {
|
||||||
}
|
node_id: layer_to_move.to_node(),
|
||||||
|
reconnect_to_sibling: true,
|
||||||
// Part 1: Disconnect layer to move and reconnect downstream node to upstream sibling if it exists.
|
});
|
||||||
self.disconnect_node(layer_to_move, responses);
|
// Reconnect layer_to_move to new parent at insert index.
|
||||||
|
responses.add(GraphOperationMessage::InsertLayerAtStackIndex {
|
||||||
// Part 2: Reconnect layer_to_move to new parent at insert index.
|
layer_id: layer_to_move.to_node(),
|
||||||
let (post_node_id, pre_node_id, post_node_input_index) = ModifyInputsContext::get_post_node_with_index(&self.network, parent.to_node(), insert_index);
|
parent: 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if run_document_graph_after {
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
|
||||||
}
|
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
}
|
}
|
||||||
DocumentMessage::NudgeSelectedLayers {
|
DocumentMessage::NudgeSelectedLayers {
|
||||||
@@ -736,7 +598,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
let image_frame = ImageFrame { image, ..Default::default() };
|
let image_frame = ImageFrame { image, ..Default::default() };
|
||||||
|
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
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()] });
|
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;
|
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 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 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(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||||
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
|
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
|
||||||
}
|
}
|
||||||
@@ -994,14 +856,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
DocumentMessage::UngroupSelectedLayers => {
|
DocumentMessage::UngroupSelectedLayers => {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
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()));
|
let folder_paths = self.metadata().folders_sorted_by_most_nested(self.selected_nodes.selected_layers(self.metadata()));
|
||||||
for folder in folder_paths {
|
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
|
// Cannot ungroup artboard
|
||||||
let folder_node = self.network.nodes.get(&folder.to_node()).expect("Folder node should always exist");
|
let folder_node = self.network.nodes.get(&folder.to_node()).expect("Folder node should always exist");
|
||||||
if folder_node.is_artboard() {
|
if folder_node.is_artboard() {
|
||||||
@@ -1017,30 +873,29 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
// Move child_layer stack x position to folder stack
|
// 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 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 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);
|
responses.add(NodeGraphMessage::ShiftUpstream {
|
||||||
modify_inputs.shift_upstream(child_layer_node_id, offset, true);
|
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 {
|
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");
|
log::error!("Downstream node should always exist when moving layer");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let Some(NodeInput::Node { node_id, .. }) = self
|
|
||||||
.network
|
// Output_index must be 0 since layers only have 1 output
|
||||||
.nodes
|
let downstream_input = NodeInput::node(child_layer_node_id, 0);
|
||||||
.get_mut(&downstream_node_id)
|
responses.add(NodeGraphMessage::SetNodeInput {
|
||||||
.expect("downstream node should always exist")
|
node_id: downstream_node_id,
|
||||||
.inputs
|
input_index: downstream_input_index,
|
||||||
.get_mut(downstream_input_index)
|
input: downstream_input,
|
||||||
else {
|
});
|
||||||
log::error!("Could not get downstream node input");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
*node_id = child_layer_node_id;
|
|
||||||
|
|
||||||
// Get the node that feeds into the primary input for the folder (if it exists)
|
// 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) {
|
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
|
// Get the node at the bottom of the first layer node stack
|
||||||
let mut last_child_node_id = child_layer_node_id;
|
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;
|
last_child_node_id = *node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect the primary input of the bottom layer to the node to the upstream sibling
|
// Connect the primary input of the bottom layer of 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 {
|
let bottom_layer_node_input = NodeInput::node(layer_upstream_sibling_id, 0);
|
||||||
log::error!("Could not get last child node primary input");
|
responses.add(NodeGraphMessage::SetNodeInput {
|
||||||
continue;
|
node_id: last_child_node_id,
|
||||||
};
|
input_index: 0,
|
||||||
*node_input = NodeInput::node(upstream_sibling_id, 0);
|
input: bottom_layer_node_input,
|
||||||
|
});
|
||||||
|
|
||||||
// Shift upstream_sibling down by the height of the child layer stack
|
// 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");
|
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 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 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 current_distance = upstream_sibling_node.metadata.position.y - folder_node.metadata.position.y;
|
||||||
|
|
||||||
let y_offset = target_distance - current_distance + 3;
|
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);
|
responses.add(NodeGraphMessage::ShiftUpstream {
|
||||||
modify_inputs.shift_upstream(upstream_sibling_id, IVec2::new(0, y_offset), true);
|
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 {
|
responses.add(NodeGraphMessage::DeleteNodes {
|
||||||
node_ids: vec![folder.to_node()],
|
node_ids: vec![folder.to_node()],
|
||||||
reconnect: true,
|
reconnect: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if run_document_graph_after {
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
|
||||||
}
|
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
}
|
}
|
||||||
@@ -1385,23 +1242,6 @@ impl DocumentMessageHandler {
|
|||||||
self.saved_hash = None;
|
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)> {
|
pub fn get_downstream_node(network: &NodeNetwork, metadata: &DocumentMetadata, layer_to_move: LayerNodeIdentifier) -> Option<(NodeId, usize)> {
|
||||||
let mut downstream_layer = None;
|
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).
|
/// 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.
|
/// 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.
|
/// 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()
|
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())
|
.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.
|
/// 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>) {
|
pub fn load_layer_resources(&self, responses: &mut VecDeque<Message>) {
|
||||||
let mut fonts = HashSet::new();
|
let mut fonts = HashSet::new();
|
||||||
|
|||||||
@@ -19,10 +19,36 @@ use glam::{DAffine2, DVec2, IVec2};
|
|||||||
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
#[impl_message(Message, DocumentMessage, GraphOperation)]
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum GraphOperationMessage {
|
pub enum GraphOperationMessage {
|
||||||
|
AddNodesAsChild {
|
||||||
|
nodes: HashMap<NodeId, DocumentNode>,
|
||||||
|
parent: LayerNodeIdentifier,
|
||||||
|
insert_index: isize,
|
||||||
|
},
|
||||||
|
DisconnectInput {
|
||||||
|
node_id: NodeId,
|
||||||
|
input_index: usize,
|
||||||
|
},
|
||||||
FillSet {
|
FillSet {
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
fill: Fill,
|
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 {
|
OpacitySet {
|
||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
opacity: f64,
|
opacity: f64,
|
||||||
@@ -64,10 +90,6 @@ pub enum GraphOperationMessage {
|
|||||||
layer: LayerNodeIdentifier,
|
layer: LayerNodeIdentifier,
|
||||||
strokes: Vec<BrushStroke>,
|
strokes: Vec<BrushStroke>,
|
||||||
},
|
},
|
||||||
MoveUpstreamSiblingsToChild {
|
|
||||||
new_parent: NodeId,
|
|
||||||
upstream_sibling_ids: Vec<NodeId>,
|
|
||||||
},
|
|
||||||
NewArtboard {
|
NewArtboard {
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
artboard: Artboard,
|
artboard: Artboard,
|
||||||
|
|||||||
+244
-58
@@ -1,5 +1,6 @@
|
|||||||
use super::transform_utils::{self, LayerBounds};
|
use super::transform_utils::{self, LayerBounds};
|
||||||
use super::utility_types::ModifyInputsContext;
|
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::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, SelectedNodes};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
@@ -36,11 +37,185 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
match message {
|
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 } => {
|
GraphOperationMessage::FillSet { layer, fill } => {
|
||||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
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);
|
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 } => {
|
GraphOperationMessage::OpacitySet { layer, opacity } => {
|
||||||
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
|
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);
|
modify_inputs.opacity_set(opacity);
|
||||||
@@ -103,67 +278,78 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||||||
modify_inputs.brush_modify(strokes);
|
modify_inputs.brush_modify(strokes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GraphOperationMessage::MoveUpstreamSiblingsToChild { new_parent, upstream_sibling_ids } => {
|
GraphOperationMessage::MoveSelectedSiblingsToChild { new_parent } => {
|
||||||
// Start with the furthest upstream node, move it as a child of the new folder, and continue downstream for each layer in vec
|
let group_layer = LayerNodeIdentifier::new(new_parent, &document_network);
|
||||||
for node_to_move in upstream_sibling_ids.iter().rev() {
|
let Some(group_parent) = group_layer.parent(&document_metadata) else {
|
||||||
// Connect pre node to post node, or disconnect pre node if post node doesn't exist
|
log::error!("Could not find parent for layer {:?}", group_layer);
|
||||||
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 {
|
|
||||||
return;
|
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");
|
// 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 upstream_shift = IVec2::new(-8, 0);
|
let mut selected_siblings = Vec::new();
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
|
||||||
modify_inputs.shift_upstream(*top_of_stack, upstream_shift, true);
|
// 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 } => {
|
GraphOperationMessage::NewArtboard { id, artboard } => {
|
||||||
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
let mut modify_inputs = ModifyInputsContext::new(document_network, document_metadata, node_graph, responses);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
|
use glam::IVec2;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
@@ -36,6 +37,10 @@ pub enum NodeGraphMessage {
|
|||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
},
|
},
|
||||||
|
DisconnectLayerFromStack {
|
||||||
|
node_id: NodeId,
|
||||||
|
reconnect_to_sibling: bool,
|
||||||
|
},
|
||||||
EnterNestedNetwork {
|
EnterNestedNetwork {
|
||||||
node: NodeId,
|
node: NodeId,
|
||||||
},
|
},
|
||||||
@@ -92,6 +97,10 @@ pub enum NodeGraphMessage {
|
|||||||
input_index: usize,
|
input_index: usize,
|
||||||
input: NodeInput,
|
input: NodeInput,
|
||||||
},
|
},
|
||||||
|
SetNodePosition {
|
||||||
|
node_id: NodeId,
|
||||||
|
position: IVec2,
|
||||||
|
},
|
||||||
SetQualifiedInputValue {
|
SetQualifiedInputValue {
|
||||||
node_path: Vec<NodeId>,
|
node_path: Vec<NodeId>,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
@@ -101,6 +110,11 @@ pub enum NodeGraphMessage {
|
|||||||
ShiftNode {
|
ShiftNode {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
|
ShiftUpstream {
|
||||||
|
node_id: NodeId,
|
||||||
|
shift: IVec2,
|
||||||
|
shift_self: bool,
|
||||||
|
},
|
||||||
SetVisibility {
|
SetVisibility {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::application::generate_uuid;
|
|||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
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::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::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::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry, SelectedNodes};
|
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();
|
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) {
|
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,
|
// This does a downstream traversal starting from the current node, and ending at either a node in the delete_nodes set or the output.
|
||||||
// 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,
|
// 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.
|
||||||
// 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.
|
|
||||||
|
|
||||||
let mut stack = vec![upstream_id];
|
let mut stack = vec![upstream_id];
|
||||||
let mut can_delete = true;
|
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() {
|
while let Some(current_node) = stack.pop() {
|
||||||
if let Some(downstream_nodes) = outward_links.get(¤t_node) {
|
if let Some(downstream_nodes) = outward_links.get(¤t_node) {
|
||||||
for downstream_node in downstream_nodes {
|
for downstream_node in downstream_nodes {
|
||||||
@@ -238,35 +235,65 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DisconnectNodes { node_id, input_index } => {
|
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 {
|
let Some(network) = document_network.nested_network(&self.network) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
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) {
|
if network.connected_to_output(node_id) {
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
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 } => {
|
NodeGraphMessage::EnterNestedNetwork { node } => {
|
||||||
if let Some(network) = document_network.nested_network(&self.network) {
|
if let Some(network) = document_network.nested_network(&self.network) {
|
||||||
if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() {
|
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");
|
error!("No network");
|
||||||
return;
|
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);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
let post_input = NodeInput::node(insert_node_id, insert_node_output_index);
|
responses.add(GraphOperationMessage::InsertNodeBetween {
|
||||||
responses.add(NodeGraphMessage::SetNodeInput {
|
post_node_id,
|
||||||
node_id: post_node_id,
|
post_node_input_index,
|
||||||
input_index: post_node_input_index,
|
insert_node_output_index,
|
||||||
input: post_input,
|
insert_node_id,
|
||||||
});
|
insert_node_input_index,
|
||||||
|
pre_node_output_index,
|
||||||
let insert_input = NodeInput::node(pre_node_id, pre_node_output_index);
|
pre_node_id,
|
||||||
responses.add(NodeGraphMessage::SetNodeInput {
|
|
||||||
node_id: insert_node_id,
|
|
||||||
input_index: insert_node_input_index,
|
|
||||||
input: insert_input,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if network.connected_to_output(insert_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 } => {
|
NodeGraphMessage::SetQualifiedInputValue { node_path, input_index, value } => {
|
||||||
let Some((node_id, node_path)) = node_path.split_last() else {
|
let Some((node_id, node_path)) = node_path.split_last() else {
|
||||||
error!("Node path is empty");
|
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);
|
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 => {
|
NodeGraphMessage::ToggleSelectedVisibility => {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
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();
|
let ordered_last_elements: Vec<_> = active_document.metadata.all_layers().filter(|layer| get_last_elements.contains(&layer)).collect();
|
||||||
|
|
||||||
for layer in ordered_last_elements {
|
for layer in ordered_last_elements {
|
||||||
let node = layer.to_node();
|
let layer_node_id = layer.to_node();
|
||||||
let previous_alias = active_document.network().nodes.get(&node).map(|node| node.alias.clone()).unwrap_or_default();
|
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()
|
.network()
|
||||||
.nodes
|
.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(|node| if node.is_layer { node.inputs.get(1) } else { node.inputs.get(0) })
|
||||||
.and_then(|input| input.as_node())
|
.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 {
|
buffer.push(CopyBufferEntry {
|
||||||
nodes: NodeGraphMessageHandler::copy_nodes(
|
nodes: NodeGraphMessageHandler::copy_nodes(active_document.network(), ©_ids).collect(),
|
||||||
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(),
|
|
||||||
selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()),
|
selected: active_document.selected_nodes.selected_layers_contains(layer, active_document.metadata()),
|
||||||
visible: active_document.selected_nodes.layer_visible(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()),
|
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<_>| {
|
let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| {
|
||||||
if self.active_document().is_some() {
|
if self.active_document().is_some() {
|
||||||
trace!("Pasting into folder {parent:?} as index: {insert_index}");
|
trace!("Pasting into folder {parent:?} as index: {insert_index}");
|
||||||
let id = NodeId(generate_uuid());
|
|
||||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
responses.add(GraphOperationMessage::AddNodesAsChild {
|
||||||
id,
|
nodes: entry.clone().nodes,
|
||||||
nodes: entry.nodes.clone(),
|
|
||||||
parent,
|
parent,
|
||||||
insert_index,
|
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() {
|
for entry in self.copy_buffer[clipboard as usize].iter().rev() {
|
||||||
paste(entry, responses)
|
paste(entry, responses)
|
||||||
}
|
}
|
||||||
@@ -411,30 +401,19 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
PortfolioMessage::PasteSerializedData { data } => {
|
PortfolioMessage::PasteSerializedData { data } => {
|
||||||
if let Some(document) = self.active_document() {
|
if let Some(document) = self.active_document() {
|
||||||
if let Ok(data) = serde_json::from_str::<Vec<CopyBufferEntry>>(&data) {
|
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::DeselectAllLayers);
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
for entry in data.into_iter().rev() {
|
for entry in data.into_iter().rev() {
|
||||||
document.load_layer_resources(responses);
|
document.load_layer_resources(responses);
|
||||||
let id = NodeId(generate_uuid());
|
responses.add(GraphOperationMessage::AddNodesAsChild {
|
||||||
responses.add(GraphOperationMessage::NewCustomLayer {
|
|
||||||
id,
|
|
||||||
nodes: entry.nodes,
|
nodes: entry.nodes,
|
||||||
parent,
|
parent,
|
||||||
insert_index: -1,
|
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 {
|
responses.add(GraphOperationMessage::NewCustomLayer {
|
||||||
id,
|
id,
|
||||||
nodes: HashMap::from([(NodeId(0), brush_node)]),
|
nodes: HashMap::from([(NodeId(0), brush_node)]),
|
||||||
parent: document.new_layer_parent(),
|
parent: document.new_layer_parent(true),
|
||||||
insert_index: -1,
|
insert_index: -1,
|
||||||
alias: String::new(),
|
alias: String::new(),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ impl Fsm for EllipseToolFsmState {
|
|||||||
// Create a new ellipse vector shape
|
// Create a new ellipse vector shape
|
||||||
let subpath = bezier_rs::Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE);
|
let subpath = bezier_rs::Subpath::new_ellipse(DVec2::ZERO, DVec2::ONE);
|
||||||
let manipulator_groups = subpath.manipulator_groups().to_vec();
|
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);
|
graph_modification_utils::set_manipulator_colinear_handles_state(&manipulator_groups, layer, true, responses);
|
||||||
shape_data.layer = Some(layer);
|
shape_data.layer = Some(layer);
|
||||||
|
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
} else {
|
} else {
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
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 transform = document.metadata().transform_to_viewport(parent);
|
||||||
let pos = transform.inverse().transform_point2(input.mouse.position);
|
let pos = transform.inverse().transform_point2(input.mouse.position);
|
||||||
let subpath = bezier_rs::Subpath::from_anchors([pos], false);
|
let subpath = bezier_rs::Subpath::from_anchors([pos], false);
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ impl Fsm for LineToolFsmState {
|
|||||||
|
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
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 {
|
responses.add(GraphOperationMessage::StrokeSet {
|
||||||
layer,
|
layer,
|
||||||
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ impl PenToolData {
|
|||||||
input: &InputPreprocessorMessageHandler,
|
input: &InputPreprocessorMessageHandler,
|
||||||
responses: &mut VecDeque<Message>,
|
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
|
// Deselect layers because we are now creating a new layer
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
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::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),
|
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);
|
polygon_data.layer = Some(layer);
|
||||||
|
|
||||||
let fill_color = tool_options.fill.active_color();
|
let fill_color = tool_options.fill.active_color();
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ impl Fsm for RectangleToolFsmState {
|
|||||||
|
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
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);
|
shape_data.layer = Some(layer);
|
||||||
|
|
||||||
let fill_color = tool_options.fill.active_color();
|
let fill_color = tool_options.fill.active_color();
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ impl Fsm for SplineToolFsmState {
|
|||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
responses.add(DocumentMessage::DeselectAllLayers);
|
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 transform = document.metadata().transform_to_viewport(parent);
|
||||||
|
|
||||||
//tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
|
//tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ impl TextToolData {
|
|||||||
text: String::new(),
|
text: String::new(),
|
||||||
font: editing_text.font.clone(),
|
font: editing_text.font.clone(),
|
||||||
size: editing_text.font_size,
|
size: editing_text.font_size,
|
||||||
parent: document.new_layer_parent(),
|
parent: document.new_layer_parent(true),
|
||||||
insert_index: -1,
|
insert_index: -1,
|
||||||
});
|
});
|
||||||
responses.add(GraphOperationMessage::FillSet {
|
responses.add(GraphOperationMessage::FillSet {
|
||||||
|
|||||||
Reference in New Issue
Block a user