Allow groups to work with the node graph (#1452)

* Initial groups

* Improve graph arangement

* Fix selecting nested layers

* Code review pass

* Change log

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0HyperCube
2023-11-14 17:17:14 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent f4ec76f35e
commit 58660f5548
19 changed files with 397 additions and 306 deletions
+32 -2
View File
@@ -77,6 +77,7 @@ impl DocumentMetadata {
self.structure.entry(node_identifier).or_default() self.structure.entry(node_identifier).or_default()
} }
/// Layers excluding ones that are children of other layers in the list.
pub fn shallowest_unique_layers(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<Vec<LayerNodeIdentifier>> { pub fn shallowest_unique_layers(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<Vec<LayerNodeIdentifier>> {
let mut sorted_layers = layers let mut sorted_layers = layers
.map(|layer| { .map(|layer| {
@@ -90,6 +91,34 @@ impl DocumentMetadata {
sorted_layers.dedup_by(|a, b| a.starts_with(b)); sorted_layers.dedup_by(|a, b| a.starts_with(b));
sorted_layers sorted_layers
} }
/// Ancestor that is shared by all layers and that is deepest (more nested). May be the root layer.
pub fn deepest_common_ancestor(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> LayerNodeIdentifier {
layers
.map(|layer| {
let mut layer_path = layer.ancestors(self).skip(1).collect::<Vec<_>>();
layer_path.reverse();
layer_path
})
.reduce(|mut a, b| {
a.truncate(a.iter().zip(b.iter()).position(|(&a, &b)| a != b).unwrap_or_else(|| a.len().min(b.len())));
a
})
.and_then(|path| path.last().copied())
.unwrap_or(LayerNodeIdentifier::ROOT)
}
/// Filter out non folder layers
pub fn folders<'a>(&'a self, layers: impl Iterator<Item = LayerNodeIdentifier> + 'a) -> impl Iterator<Item = LayerNodeIdentifier> + 'a {
layers.filter(|layer| layer.has_children(self))
}
/// Folders sorted from most nested to least nested
pub fn folders_sorted_by_most_nested(&self, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Vec<LayerNodeIdentifier> {
let mut folders: Vec<_> = self.folders(layers).collect();
folders.sort_by_cached_key(|a| std::cmp::Reverse(a.ancestors(self).count()));
folders
}
} }
// selected layer modifications // selected layer modifications
@@ -146,7 +175,7 @@ impl DocumentMetadata {
} }
fn first_child_layer<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> { fn first_child_layer<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> {
graph.primary_flow_from_opt(Some(node.inputs[0].as_node()?)).find(|(node, _)| node.name == "Layer") graph.primary_flow_from_node(Some(node.inputs[0].as_node()?)).find(|(node, _)| node.name == "Layer")
} }
fn sibling_below<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> { fn sibling_below<'a>(graph: &'a NodeNetwork, node: &DocumentNode) -> Option<(&'a DocumentNode, NodeId)> {
node.inputs[7].as_node().and_then(|id| graph.nodes.get(&id).filter(|node| node.name == "Layer").map(|node| (node, id))) node.inputs[7].as_node().and_then(|id| graph.nodes.get(&id).filter(|node| node.name == "Layer").map(|node| (node, id)))
@@ -178,7 +207,7 @@ impl DocumentMetadata {
} }
fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool { fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool {
network.primary_flow_from_opt(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard") network.primary_flow_from_node(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard")
} }
// click targets // click targets
@@ -299,6 +328,7 @@ impl LayerNodeIdentifier {
} }
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node /// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node
#[track_caller]
pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self { pub fn new(node_id: NodeId, network: &NodeNetwork) -> Self {
debug_assert!( debug_assert!(
is_layer_node(node_id, network), is_layer_node(node_id, network),
-6
View File
@@ -22,11 +22,6 @@ pub enum DocumentResponse {
LayerChanged { LayerChanged {
path: Vec<LayerId>, path: Vec<LayerId>,
}, },
MoveSelectedLayersTo {
folder_path: Vec<LayerId>,
insert_index: isize,
reverse_index: bool,
},
DeletedSelectedManipulatorPoints, DeletedSelectedManipulatorPoints,
} }
@@ -39,7 +34,6 @@ impl fmt::Display for DocumentResponse {
DocumentResponse::LayerChanged { .. } => write!(f, "LayerChanged"), DocumentResponse::LayerChanged { .. } => write!(f, "LayerChanged"),
DocumentResponse::DeletedLayer { .. } => write!(f, "DeleteLayer"), DocumentResponse::DeletedLayer { .. } => write!(f, "DeleteLayer"),
DocumentResponse::DeletedSelectedManipulatorPoints { .. } => write!(f, "DeletedSelectedManipulatorPoints"), DocumentResponse::DeletedSelectedManipulatorPoints { .. } => write!(f, "DeletedSelectedManipulatorPoints"),
DocumentResponse::MoveSelectedLayersTo { .. } => write!(f, "MoveSelectedLayersTo"),
} }
} }
} }
+6 -6
View File
@@ -302,7 +302,7 @@ mod test {
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }); editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().document_legacy.clone(); let document_after_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().document_legacy.clone();
@@ -341,7 +341,7 @@ mod test {
editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal }); editor.handle_message(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
@@ -407,12 +407,12 @@ mod test {
editor.handle_message(DocumentMessage::DeleteSelectedLayers); editor.handle_message(DocumentMessage::DeleteSelectedLayers);
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
@@ -479,12 +479,12 @@ mod test {
editor.draw_rect(0., 800., 12., 200.); editor.draw_rect(0., 800., 12., 200.);
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
editor.handle_message(PortfolioMessage::PasteIntoFolder { editor.handle_message(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: vec![], parent: LayerNodeIdentifier::ROOT,
insert_index: -1, insert_index: -1,
}); });
@@ -5,6 +5,7 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
use crate::messages::prelude::*; use crate::messages::prelude::*;
use document_legacy::document::Document as DocumentLegacy; use document_legacy::document::Document as DocumentLegacy;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::layers::blend_mode::BlendMode; use document_legacy::layers::blend_mode::BlendMode;
use document_legacy::layers::style::ViewMode; use document_legacy::layers::style::ViewMode;
use document_legacy::LayerId; use document_legacy::LayerId;
@@ -106,9 +107,8 @@ pub enum DocumentMessage {
affected_layer_path: Vec<LayerId>, affected_layer_path: Vec<LayerId>,
}, },
MoveSelectedLayersTo { MoveSelectedLayersTo {
folder_path: Vec<LayerId>, parent: LayerNodeIdentifier,
insert_index: isize, insert_index: isize,
reverse_index: bool,
}, },
NudgeSelectedLayers { NudgeSelectedLayers {
delta_x: f64, delta_x: f64,
@@ -169,6 +169,9 @@ pub enum DocumentMessage {
SetOverlaysVisibility { SetOverlaysVisibility {
visible: bool, visible: bool,
}, },
SetRangeSelectionLayer {
new_layer: Option<LayerNodeIdentifier>,
},
SetSelectedLayers { SetSelectedLayers {
replacement_selected_layers: Vec<Vec<LayerId>>, replacement_selected_layers: Vec<Vec<LayerId>>,
}, },
@@ -189,9 +192,6 @@ pub enum DocumentMessage {
}, },
Undo, Undo,
UndoFinished, UndoFinished,
UngroupLayers {
folder_path: Vec<LayerId>,
},
UngroupSelectedLayers, UngroupSelectedLayers,
UpdateDocumentTransform { UpdateDocumentTransform {
transform: glam::DAffine2, transform: glam::DAffine2,
@@ -55,7 +55,7 @@ pub struct DocumentMessageHandler {
#[serde(with = "vectorize_layer_metadata")] #[serde(with = "vectorize_layer_metadata")]
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>, pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
layer_range_selection_reference: Vec<LayerId>, layer_range_selection_reference: Option<LayerNodeIdentifier>,
navigation_handler: NavigationMessageHandler, navigation_handler: NavigationMessageHandler,
#[serde(skip)] #[serde(skip)]
@@ -88,7 +88,7 @@ impl Default for DocumentMessageHandler {
undo_in_progress: false, undo_in_progress: false,
layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(), layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(),
layer_range_selection_reference: Vec::new(), layer_range_selection_reference: None,
navigation_handler: NavigationMessageHandler::default(), navigation_handler: NavigationMessageHandler::default(),
overlays_message_handler: OverlaysMessageHandler::default(), overlays_message_handler: OverlaysMessageHandler::default(),
@@ -134,15 +134,6 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
self.layer_metadata.remove(path); self.layer_metadata.remove(path);
} }
DocumentResponse::LayerChanged { path } => responses.add(LayerChanged { affected_layer_path: path.clone() }), DocumentResponse::LayerChanged { path } => responses.add(LayerChanged { affected_layer_path: path.clone() }),
DocumentResponse::MoveSelectedLayersTo {
folder_path,
insert_index,
reverse_index,
} => responses.add(MoveSelectedLayersTo {
folder_path: folder_path.clone(),
insert_index: *insert_index,
reverse_index: *reverse_index,
}),
DocumentResponse::CreatedLayer { .. } => { DocumentResponse::CreatedLayer { .. } => {
unimplemented!("We should no longer be creating layers in the document and should instead be using the node graph.") unimplemented!("We should no longer be creating layers in the document and should instead be using the node graph.")
} }
@@ -318,7 +309,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
DeselectAllLayers => { DeselectAllLayers => {
responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] }); responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] });
self.layer_range_selection_reference.clear(); self.layer_range_selection_reference = None;
} }
DirtyRenderDocument => { DirtyRenderDocument => {
// Mark all non-overlay caches as dirty // Mark all non-overlay caches as dirty
@@ -350,7 +341,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
DuplicateSelectedLayers => { DuplicateSelectedLayers => {
self.backup(responses); self.backup(responses);
responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] }); responses.add_front(SetSelectedLayers { replacement_selected_layers: vec![] });
self.layer_range_selection_reference.clear(); self.layer_range_selection_reference = None;
for path in self.selected_layers_sorted() { for path in self.selected_layers_sorted() {
responses.add(DocumentOperation::DuplicateLayer { path: path.to_vec() }); responses.add(DocumentOperation::DuplicateLayer { path: path.to_vec() });
} }
@@ -439,30 +430,26 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
} }
GroupSelectedLayers => { GroupSelectedLayers => {
// TODO: Add code that changes the insert index of the new folder based on the selected layer // TODO: Add code that changes the insert index of the new folder based on the selected layer
let mut new_folder_path = self.document_legacy.shallowest_common_folder(self.selected_layers()).unwrap_or(&[]).to_vec(); let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers());
// Required for grouping parent folders with their own children let folder_id = generate_uuid();
if !new_folder_path.is_empty() && self.selected_layers_contains(&new_folder_path) {
new_folder_path.remove(new_folder_path.len() - 1);
}
new_folder_path.push(generate_uuid());
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal }); responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
responses.add(DocumentMessage::DeleteSelectedLayers); responses.add(DocumentMessage::DeleteSelectedLayers);
responses.add(DocumentOperation::CreateFolder {
path: new_folder_path.clone(), responses.add(GraphOperationMessage::NewCustomLayer {
id: folder_id,
nodes: HashMap::new(),
parent,
insert_index: -1, insert_index: -1,
}); });
responses.add(DocumentMessage::ToggleLayerExpansion { layer_path: new_folder_path.clone() });
responses.add(PortfolioMessage::PasteIntoFolder { responses.add(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path: new_folder_path.clone(), parent: LayerNodeIdentifier::new_unchecked(folder_id),
insert_index: -1, insert_index: -1,
}); });
responses.add(DocumentMessage::SetSelectedLayers {
replacement_selected_layers: vec![new_folder_path], responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![folder_id] });
});
} }
ImaginateClear { layer_path } => responses.add(InputFrameRasterizeRegionBelowLayer { layer_path }), ImaginateClear { layer_path } => responses.add(InputFrameRasterizeRegionBelowLayer { layer_path }),
ImaginateGenerate { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }), ImaginateGenerate { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }),
@@ -496,25 +483,21 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path }); responses.add(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path });
self.update_layer_tree_options_bar_widgets(responses, &render_data); self.update_layer_tree_options_bar_widgets(responses, &render_data);
} }
MoveSelectedLayersTo { MoveSelectedLayersTo { parent, insert_index } => {
folder_path, let selected_layers = self.metadata().selected_layers().collect::<Vec<_>>();
insert_index,
reverse_index,
} => {
let selected_layers = self.selected_layers().collect::<Vec<_>>();
// Prevent trying to insert into self // Disallow trying to insert into self
if selected_layers.iter().any(|layer| folder_path.starts_with(layer)) { if selected_layers.iter().any(|&layer| parent.ancestors(self.metadata()).any(|ancestor| ancestor == layer)) {
return; return;
} }
let insert_index = self.update_insert_index(&selected_layers, &folder_path, insert_index, reverse_index).unwrap(); let insert_index = self.update_insert_index(&selected_layers, parent, insert_index).unwrap();
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal }); responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
responses.add(DocumentMessage::DeleteSelectedLayers); responses.add(DocumentMessage::DeleteSelectedLayers);
responses.add(PortfolioMessage::PasteIntoFolder { responses.add(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal, clipboard: Clipboard::Internal,
folder_path, parent,
insert_index, insert_index,
}); });
} }
@@ -700,42 +683,47 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
self.selected_layers_reorder(relative_index_offset, responses); self.selected_layers_reorder(relative_index_offset, responses);
} }
SelectLayer { layer_path, ctrl, shift } => { SelectLayer { layer_path, ctrl, shift } => {
let mut paths = vec![]; let clicked_node = *layer_path.last().expect("Cannot select root");
let last_selection_exists = !self.layer_range_selection_reference.is_empty(); let layer = LayerNodeIdentifier::new(clicked_node, self.network());
let mut nodes = vec![];
// If we have shift pressed and a layer already selected then fill the range // If we have shift pressed and a layer already selected then fill the range
if shift && last_selection_exists { if let Some(last_selected) = self.layer_range_selection_reference.filter(|_| shift) {
nodes.push(last_selected.to_node());
nodes.push(clicked_node);
// Fill the selection range // Fill the selection range
self.layer_metadata self.metadata()
.iter() .all_layers()
.filter(|(target, _)| self.document_legacy.layer_is_between(target, &layer_path, &self.layer_range_selection_reference)) .skip_while(|&node| node != layer && node != last_selected)
.for_each(|(layer_path, _)| { .skip(1)
paths.push(layer_path.clone()); .take_while(|&node| node != layer && node != last_selected)
}); .for_each(|node| nodes.push(node.to_node()));
} else { } else {
if ctrl { if ctrl {
// Toggle selection when holding ctrl // Toggle selection when holding ctrl
let layer = self.layer_metadata_mut(&layer_path); if self.metadata().selected_layers_contains(layer) {
layer.selected = !layer.selected; responses.add_front(NodeGraphMessage::SelectedNodesRemove { nodes: vec![clicked_node] });
responses.add(LayerChanged { } else {
affected_layer_path: layer_path.clone(), responses.add_front(NodeGraphMessage::SelectedNodesAdd { nodes: vec![clicked_node] });
}); }
responses.add(BroadcastEvent::SelectionChanged); responses.add(BroadcastEvent::SelectionChanged);
} else { } else {
paths.push(layer_path.clone()); nodes.push(clicked_node);
} }
// Set our last selection reference // Set our last selection reference
self.layer_range_selection_reference = layer_path; self.layer_range_selection_reference = Some(layer);
} }
// Don't create messages for empty operations // Don't create messages for empty operations
if !paths.is_empty() { if !nodes.is_empty() {
// Add or set our selected layers // Add or set our selected layers
if ctrl { if ctrl {
responses.add_front(AddSelectedLayers { additional_layers: paths }); responses.add_front(NodeGraphMessage::SelectedNodesAdd { nodes });
} else { } else {
responses.add_front(SetSelectedLayers { replacement_selected_layers: paths }); responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes });
} }
} }
} }
@@ -802,6 +790,9 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(OverlaysMessage::ClearAllOverlays); responses.add(OverlaysMessage::ClearAllOverlays);
responses.add(OverlaysMessage::Rerender); responses.add(OverlaysMessage::Rerender);
} }
SetRangeSelectionLayer { new_layer } => {
self.layer_range_selection_reference = new_layer;
}
SetSelectedLayers { replacement_selected_layers } => { SetSelectedLayers { replacement_selected_layers } => {
let selected = self.layer_metadata.iter_mut().filter(|(_, layer_metadata)| layer_metadata.selected); let selected = self.layer_metadata.iter_mut().filter(|(_, layer_metadata)| layer_metadata.selected);
selected.for_each(|(path, layer_metadata)| { selected.for_each(|(path, layer_metadata)| {
@@ -854,36 +845,28 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(UndoFinished); responses.add(UndoFinished);
} }
UndoFinished => self.undo_in_progress = false, UndoFinished => self.undo_in_progress = false,
UngroupLayers { folder_path } => {
// Select all the children of the folder
let select = self.document_legacy.folder_children_paths(&folder_path);
let message_buffer: [Message; 4] = [
// Select them
DocumentMessage::SetSelectedLayers { replacement_selected_layers: select }.into(),
// Copy them
PortfolioMessage::Copy { clipboard: Clipboard::Internal }.into(),
// Paste them into the folder above
PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal,
folder_path: folder_path[..folder_path.len() - 1].to_vec(),
insert_index: -1,
}
.into(),
// Delete the parent folder
DocumentMessage::DeleteLayer { layer_path: folder_path }.into(),
];
// Push these messages in reverse due to push_front
for message in message_buffer.into_iter().rev() {
responses.add_front(message);
}
}
UngroupSelectedLayers => { UngroupSelectedLayers => {
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
let folder_paths = self.document_legacy.sorted_folders_by_depth(self.selected_layers());
for folder_path in folder_paths { let folder_paths = self.metadata().folders_sorted_by_most_nested(self.metadata().selected_layers());
responses.add(DocumentMessage::UngroupLayers { folder_path: folder_path.to_vec() });
for folder in folder_paths {
// Select all the children of the folder
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: folder.children(self.metadata()).map(LayerNodeIdentifier::to_node).collect(),
});
// Copy them
responses.add(PortfolioMessage::Copy { clipboard: Clipboard::Internal });
// Paste them into the folder above
responses.add(PortfolioMessage::PasteIntoFolder {
clipboard: Clipboard::Internal,
parent: folder.parent(self.metadata()).unwrap_or(LayerNodeIdentifier::ROOT),
insert_index: -1,
});
// Delete the parent folder
responses.add(GraphOperationMessage::DeleteLayer { id: folder.to_node() });
} }
responses.add(DocumentMessage::CommitTransaction); responses.add(DocumentMessage::CommitTransaction);
} }
@@ -1415,12 +1398,11 @@ impl DocumentMessageHandler {
/// 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.
fn update_insert_index(&self, layers: &[&[LayerId]], path: &[LayerId], insert_index: isize, reverse_index: bool) -> Result<isize, DocumentError> { fn update_insert_index(&self, layers: &[LayerNodeIdentifier], parent: LayerNodeIdentifier, insert_index: isize) -> Result<isize, DocumentError> {
let folder = self.document_legacy.folder(path)?; let layer_ids_above = parent.children(self.metadata()).take(if insert_index < 0 { usize::MAX } else { insert_index as usize });
let insert_index = if reverse_index { folder.layer_ids.len() as isize - insert_index } else { insert_index }; let new_insert_index = layer_ids_above.filter(|layer_id| !layers.contains(layer_id)).count() as isize;
let layer_ids_above = if insert_index < 0 { &folder.layer_ids } else { &folder.layer_ids[..(insert_index as usize)] };
Ok(insert_index - layer_ids_above.iter().filter(|layer_id| layers.iter().any(|x| *x == [path, &[**layer_id]].concat())).count() as isize) Ok(new_insert_index)
} }
/// Calculates the bounding box of all layers in the document /// Calculates the bounding box of all layers in the document
@@ -1739,53 +1721,39 @@ impl DocumentMessageHandler {
pub fn selected_layers_reorder(&mut self, relative_index_offset: isize, responses: &mut VecDeque<Message>) { pub fn selected_layers_reorder(&mut self, relative_index_offset: isize, responses: &mut VecDeque<Message>) {
self.backup(responses); self.backup(responses);
let all_layer_paths = self.all_layers_sorted(); let mut selected_layers = self.metadata().selected_layers();
let selected_layers = self.selected_layers_sorted();
let first_or_last_selected_layer = match relative_index_offset.signum() { let first_or_last_selected_layer = match relative_index_offset.signum() {
-1 => selected_layers.first(), -1 => selected_layers.next(),
1 => selected_layers.last(), 1 => selected_layers.last(),
_ => panic!("selected_layers_reorder() must be given a non-zero value"), _ => panic!("selected_layers_reorder() must be given a non-zero value"),
}; };
if let Some(pivot_layer) = first_or_last_selected_layer { let Some(pivot_layer) = first_or_last_selected_layer else {
let sibling_layer_paths: Vec<_> = all_layer_paths return;
.iter() };
.filter(|layer| { let Some(parent) = pivot_layer.parent(self.metadata()) else {
// Check if this is a sibling of the pivot layer return;
// TODO: Break this out into a reusable function `fn are_layers_siblings(layer_a, layer_b) -> bool` };
let containing_folder_path = &pivot_layer[0..pivot_layer.len() - 1];
layer.starts_with(containing_folder_path) && pivot_layer.len() == layer.len()
})
.collect();
// TODO: Break this out into a reusable function: `fn layer_index_in_containing_folder(layer_path) -> usize` let sibling_layer_paths: Vec<_> = parent.children(self.metadata()).collect();
let pivot_index_among_siblings = sibling_layer_paths.iter().position(|path| *path == pivot_layer); let Some(pivot_index) = sibling_layer_paths.iter().position(|path| *path == pivot_layer) else {
return;
};
if let Some(pivot_index) = pivot_index_among_siblings { let max = sibling_layer_paths.len() as i64 - 1;
let max = sibling_layer_paths.len() as i64 - 1; let insert_index = (pivot_index as i64 + relative_index_offset as i64).clamp(0, max) as usize;
let insert_index = (pivot_index as i64 + relative_index_offset as i64).clamp(0, max) as usize;
let existing_layer_to_insert_beside = sibling_layer_paths.get(insert_index); let Some(&neighbor) = sibling_layer_paths.get(insert_index) else {
return;
};
let Some(neighbor_index) = sibling_layer_paths.iter().position(|path| *path == neighbor) else {
return;
};
// TODO: Break this block out into a call to a message called `MoveSelectedLayersNextToLayer { neighbor_path, above_or_below }` // If moving down, insert below this layer. If moving up, insert above this layer.
if let Some(neighbor_path) = existing_layer_to_insert_beside { let insert_index = if relative_index_offset < 0 { neighbor_index } else { neighbor_index + 1 } as isize;
let (neighbor_id, folder_path) = neighbor_path.split_last().expect("Can't move the root folder");
if let Some(folder) = self.document_legacy.layer(folder_path).ok().and_then(|layer| layer.as_folder().ok()) { responses.add(DocumentMessage::MoveSelectedLayersTo { parent, insert_index });
let neighbor_layer_index = folder.layer_ids.iter().position(|id| id == neighbor_id).unwrap() as isize;
// If moving down, insert below this layer. If moving up, insert above this layer.
let insert_index = if relative_index_offset < 0 { neighbor_layer_index } else { neighbor_layer_index + 1 };
responses.add(DocumentMessage::MoveSelectedLayersTo {
folder_path: folder_path.to_vec(),
insert_index,
reverse_index: false,
});
}
}
}
}
} }
} }
@@ -1,6 +1,8 @@
use crate::messages::prelude::*; use crate::messages::prelude::*;
use bezier_rs::Subpath; use bezier_rs::Subpath;
use document_legacy::document_metadata::LayerNodeIdentifier;
use graph_craft::document::DocumentNode;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graphene_core::raster::ImageFrame; use graphene_core::raster::ImageFrame;
use graphene_core::text::Font; use graphene_core::text::Font;
@@ -65,6 +67,12 @@ pub enum GraphOperationMessage {
id: NodeId, id: NodeId,
image_frame: ImageFrame<Color>, image_frame: ImageFrame<Color>,
}, },
NewCustomLayer {
id: NodeId,
nodes: HashMap<NodeId, DocumentNode>,
parent: LayerNodeIdentifier,
insert_index: isize,
},
NewVectorLayer { NewVectorLayer {
id: NodeId, id: NodeId,
subpaths: Vec<Subpath<ManipulatorGroupId>>, subpaths: Vec<Subpath<ManipulatorGroupId>>,
@@ -92,30 +92,47 @@ impl<'a> ModifyInputsContext<'a> {
Some(new_id) Some(new_id)
} }
pub fn create_layer(&mut self, new_id: NodeId, output_node_id: NodeId, input_index: usize) -> Option<NodeId> { pub fn create_layer(&mut self, new_id: NodeId, output_node_id: NodeId, input_index: usize, skip_layer_nodes: usize) -> Option<NodeId> {
assert!(!self.network.nodes.contains_key(&new_id), "Creating already existing layer"); assert!(!self.network.nodes.contains_key(&new_id), "Creating already existing layer");
let output = NodeOutput::new(output_node_id, input_index); let mut output = NodeOutput::new(output_node_id, input_index);
let mut sibling_layer = None;
let mut shift = IVec2::new(0, 3);
// Locate the node output of the first sibling layer to the new layer // Locate the node output of the first sibling layer to the new layer
let new_id = if let NodeInput::Node { node_id, output_index, .. } = &self.network.nodes.get(&output_node_id)?.inputs[input_index] { if let NodeInput::Node { node_id, output_index, .. } = &self.network.nodes.get(&output_node_id)?.inputs[input_index] {
let sibling_node = &self.network.nodes.get(node_id)?; let sibling_node = &self.network.nodes.get(node_id)?;
let node_id = *node_id; let node_id = *node_id;
let output_index = *output_index; let output_index = *output_index;
let sibling_layer = if sibling_node.name == "Layer" { if sibling_node.name == "Layer" {
// There is already a layer node // There is already a layer node
NodeOutput::new(node_id, 0) sibling_layer = Some(NodeOutput::new(node_id, 0));
} else { } else {
// The user has connected another node to the output. Insert a layer node between the output and the node. // The user has connected another node to the output. Insert a layer node between the output and the node.
let node = resolve_document_node_type("Layer").expect("Layer node").default_document_node(); let node = resolve_document_node_type("Layer").expect("Layer node").default_document_node();
let node_id = self.insert_between(generate_uuid(), NodeOutput::new(node_id, output_index), output, node, 0, 0, IVec2::new(-8, 0))?; let node_id = self.insert_between(generate_uuid(), NodeOutput::new(node_id, output_index), output, node, 0, 0, IVec2::new(-8, 0))?;
NodeOutput::new(node_id, 0) sibling_layer = Some(NodeOutput::new(node_id, 0));
}; }
let node = resolve_document_node_type("Layer").expect("Layer node").default_document_node(); // Skip some layer nodes
self.insert_between(new_id, sibling_layer, output, node, 7, 0, IVec2::new(0, 3)) for _ in 0..skip_layer_nodes {
if let Some(old_sibling) = &sibling_layer {
output = NodeOutput::new(old_sibling.node_id, 7);
sibling_layer = self.network.nodes.get(&old_sibling.node_id)?.inputs[7].as_node().map(|node| NodeOutput::new(node, 0));
shift = IVec2::new(0, 3);
}
}
// Insert at top of stack
} else { } else {
let layer_node = resolve_document_node_type("Layer").expect("Node").default_document_node(); shift = IVec2::new(-8, 3);
self.insert_node_before(new_id, output_node_id, input_index, layer_node, IVec2::new(-5, 3)) }
// Create node
let layer_node = resolve_document_node_type("Layer").expect("Layer node").default_document_node();
let new_id = if let Some(sibling_layer) = sibling_layer {
self.insert_between(new_id, sibling_layer, output, layer_node, 7, 0, shift)
} else {
self.insert_node_before(new_id, output.node_id, output.node_output_index, layer_node, shift)
}; };
// Update the document metadata structure // Update the document metadata structure
@@ -262,7 +279,7 @@ impl<'a> ModifyInputsContext<'a> {
/// Changes the inputs of a specific node /// Changes the inputs of a specific node
fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { fn modify_inputs(&mut self, name: &'static str, skip_rerender: bool, update_input: impl FnOnce(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_node_id = self.network.primary_flow_from_opt(self.layer_node).find(|(node, _)| node.name == name).map(|(_, id)| id); let existing_node_id = self.network.primary_flow_from_node(self.layer_node).find(|(node, _)| node.name == name).map(|(_, id)| id);
if let Some(node_id) = existing_node_id { if let Some(node_id) = existing_node_id {
self.modify_existing_node_inputs(node_id, update_input); self.modify_existing_node_inputs(node_id, update_input);
} else { } else {
@@ -285,7 +302,7 @@ impl<'a> ModifyInputsContext<'a> {
/// Changes the inputs of a all of the existing instances of a node name /// Changes the inputs of a all of the existing instances of a node name
fn modify_all_node_inputs(&mut self, name: &'static str, skip_rerender: bool, mut update_input: impl FnMut(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) { fn modify_all_node_inputs(&mut self, name: &'static str, skip_rerender: bool, mut update_input: impl FnMut(&mut Vec<NodeInput>, NodeId, &DocumentMetadata)) {
let existing_nodes: Vec<_> = self.network.primary_flow_from_opt(self.layer_node).filter(|(node, _)| node.name == name).map(|(_, id)| id).collect(); let existing_nodes: Vec<_> = self.network.primary_flow_from_node(self.layer_node).filter(|(node, _)| node.name == name).map(|(_, id)| id).collect();
for existing_node_id in existing_nodes { for existing_node_id in existing_nodes {
self.modify_existing_node_inputs(existing_node_id, &mut update_input); self.modify_existing_node_inputs(existing_node_id, &mut update_input);
} }
@@ -460,6 +477,7 @@ impl<'a> ModifyInputsContext<'a> {
LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata); LayerNodeIdentifier::new(id, self.network).delete(self.document_metadata);
let new_input = node.inputs[7].clone(); let new_input = node.inputs[7].clone();
let deleted_position = node.metadata.position;
for post_node in self.outwards_links.get(&id).unwrap_or(&Vec::new()) { for post_node in self.outwards_links.get(&id).unwrap_or(&Vec::new()) {
let Some(node) = self.network.nodes.get_mut(post_node) else { let Some(node) = self.network.nodes.get_mut(post_node) else {
@@ -476,7 +494,7 @@ impl<'a> ModifyInputsContext<'a> {
} }
let mut delete_nodes = vec![id]; let mut delete_nodes = vec![id];
for (_node, id) in self.network.primary_flow_from_opt(Some(id)) { for (_node, id) in self.network.primary_flow_from_node(Some(id)) {
if self.outwards_links.get(&id).is_some_and(|outwards| outwards.len() == 1) { if self.outwards_links.get(&id).is_some_and(|outwards| outwards.len() == 1) {
delete_nodes.push(id); delete_nodes.push(id);
} }
@@ -485,6 +503,16 @@ impl<'a> ModifyInputsContext<'a> {
for node_id in &delete_nodes { for node_id in &delete_nodes {
self.network.nodes.remove(node_id); self.network.nodes.remove(node_id);
} }
if let Some(node_id) = new_input.as_node() {
if let Some(shift) = self.network.nodes.get(&node_id).map(|node| deleted_position - node.metadata.position) {
for node_id in self.network.all_dependencies(node_id).map(|(_, id)| id).collect::<Vec<_>>() {
let Some(node) = self.network.nodes.get_mut(&node_id) else { continue };
node.metadata.position += shift;
}
}
}
self.responses.add(self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id))); self.responses.add(self.document_metadata.retain_selected_nodes(|id| !delete_nodes.contains(id)));
self.responses.add(DocumentMessage::DocumentStructureChanged); self.responses.add(DocumentMessage::DocumentStructureChanged);
@@ -567,25 +595,75 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
} }
GraphOperationMessage::NewArtboard { id, artboard } => { GraphOperationMessage::NewArtboard { id, artboard } => {
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_artboard(artboard, layer); modify_inputs.insert_artboard(artboard, layer);
} }
} }
GraphOperationMessage::NewBitmapLayer { id, image_frame } => { GraphOperationMessage::NewBitmapLayer { id, image_frame } => {
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_image_data(image_frame, layer); modify_inputs.insert_image_data(image_frame, layer);
} }
} }
GraphOperationMessage::NewCustomLayer { id, nodes, parent, insert_index } => {
trace!("Inserting new layer {id} as a child of {parent:?} at index {insert_index}");
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
let skip_layer_nodes = if insert_index < 0 { (-1 - insert_index) as usize } else { insert_index as usize };
let output_node_id = if parent == LayerNodeIdentifier::ROOT {
modify_inputs.network.original_outputs()[0].node_id
} else {
parent.to_node()
};
if let Some(layer) = modify_inputs.create_layer(id, output_node_id, 0, skip_layer_nodes) {
let new_ids: HashMap<_, _> = nodes.iter().map(|(&id, _)| (id, crate::application::generate_uuid())).collect();
let shift = nodes
.get(&0)
.and_then(|node| {
modify_inputs
.network
.nodes
.get(&layer)
.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
modify_inputs.network.nodes.insert(node_id, document_node);
}
if let Some(layer_node) = modify_inputs.network.nodes.get_mut(&layer) {
if let Some(&input) = new_ids.get(&0) {
layer_node.inputs[0] = NodeInput::node(input, 0)
}
}
modify_inputs.responses.add(NodeGraphMessage::SendGraph { should_rerender: true });
}
document.metadata.load_structure(&document.document_network);
}
GraphOperationMessage::NewVectorLayer { id, subpaths } => { GraphOperationMessage::NewVectorLayer { id, subpaths } => {
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_vector_data(subpaths, layer); modify_inputs.insert_vector_data(subpaths, layer);
} }
} }
GraphOperationMessage::NewTextLayer { id, text, font, size } => { GraphOperationMessage::NewTextLayer { id, text, font, size } => {
let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses);
if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0) { if let Some(layer) = modify_inputs.create_layer(id, modify_inputs.network.original_outputs()[0].node_id, 0, 0) {
modify_inputs.insert_text(text, font, size, layer); modify_inputs.insert_text(text, font, size, layer);
} }
} }
@@ -9,9 +9,6 @@ use graph_craft::document::{DocumentNode, NodeId, NodeInput};
pub enum NodeGraphMessage { pub enum NodeGraphMessage {
// Messages // Messages
Init, Init,
AddSelectNodes {
nodes: Vec<NodeId>,
},
SelectedNodesUpdated, SelectedNodesUpdated,
CloseNodeGraph, CloseNodeGraph,
ConnectNodesByLink { ConnectNodesByLink {
@@ -67,6 +64,15 @@ pub enum NodeGraphMessage {
serialized_nodes: String, serialized_nodes: String,
}, },
RunDocumentGraph, RunDocumentGraph,
SelectedNodesAdd {
nodes: Vec<NodeId>,
},
SelectedNodesRemove {
nodes: Vec<NodeId>,
},
SelectedNodesSet {
nodes: Vec<NodeId>,
},
SendGraph { SendGraph {
should_rerender: bool, should_rerender: bool,
}, },
@@ -86,9 +92,6 @@ pub enum NodeGraphMessage {
input_index: usize, input_index: usize,
value: TaggedValue, value: TaggedValue,
}, },
SetSelectedNodes {
nodes: Vec<NodeId>,
},
ShiftNode { ShiftNode {
node_id: NodeId, node_id: NodeId,
}, },
@@ -417,14 +417,14 @@ impl NodeGraphMessageHandler {
} }
/// Gets the default node input based on the node name and the input index /// Gets the default node input based on the node name and the input index
fn default_node_input(name: String, index: usize) -> Option<NodeInput> { pub fn default_node_input(name: String, index: usize) -> Option<NodeInput> {
resolve_document_node_type(&name) resolve_document_node_type(&name)
.and_then(|node| node.inputs.get(index)) .and_then(|node| node.inputs.get(index))
.map(|input: &DocumentInputType| input.default.clone()) .map(|input: &DocumentInputType| input.default.clone())
} }
/// Returns an iterator of nodes to be copied and their ids, excluding output and input nodes /// Returns an iterator of nodes to be copied and their ids, excluding output and input nodes
fn copy_nodes<'a>(network: &'a NodeNetwork, new_ids: &'a HashMap<NodeId, NodeId>) -> impl Iterator<Item = (NodeId, DocumentNode)> + 'a { pub fn copy_nodes<'a>(network: &'a NodeNetwork, new_ids: &'a HashMap<NodeId, NodeId>) -> impl Iterator<Item = (NodeId, DocumentNode)> + 'a {
new_ids new_ids
.iter() .iter()
.filter(|&(&id, _)| !network.outputs_contain(id)) .filter(|&(&id, _)| !network.outputs_contain(id))
@@ -455,12 +455,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
document.metadata.load_structure(&document.document_network); document.metadata.load_structure(&document.document_network);
responses.add(DocumentMessage::DocumentStructureChanged); responses.add(DocumentMessage::DocumentStructureChanged);
} }
NodeGraphMessage::AddSelectNodes { nodes } => {
responses.add(document.metadata.add_selected_nodes(nodes));
}
NodeGraphMessage::SelectedNodesUpdated => { NodeGraphMessage::SelectedNodesUpdated => {
self.update_selection_action_buttons(document, responses); self.update_selection_action_buttons(document, responses);
self.update_selected(document, responses); self.update_selected(document, responses);
if document.metadata.selected_layers().count() <= 1 {
responses.add(DocumentMessage::SetRangeSelectionLayer {
new_layer: document.metadata.selected_layers().next(),
});
}
responses.add(NodeGraphMessage::RunDocumentGraph);
} }
NodeGraphMessage::CloseNodeGraph => {} NodeGraphMessage::CloseNodeGraph => {}
NodeGraphMessage::ConnectNodesByLink { NodeGraphMessage::ConnectNodesByLink {
@@ -739,11 +742,21 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} }
let nodes = new_ids.values().copied().collect(); let nodes = new_ids.values().copied().collect();
responses.add(NodeGraphMessage::SetSelectedNodes { nodes }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes });
responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
} }
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path: Vec::new() }), NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path: Vec::new() }),
NodeGraphMessage::SelectedNodesAdd { nodes } => {
responses.add(document.metadata.add_selected_nodes(nodes));
}
NodeGraphMessage::SelectedNodesRemove { nodes } => {
responses.add(document.metadata.retain_selected_nodes(|node| !nodes.contains(node)));
}
NodeGraphMessage::SelectedNodesSet { nodes } => {
responses.add(document.metadata.set_selected_nodes(nodes));
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
NodeGraphMessage::SendGraph { should_rerender } => { NodeGraphMessage::SendGraph { should_rerender } => {
if let Some(network) = document.document_network.nested_network(&self.network) { if let Some(network) = document.document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, responses); Self::send_graph(network, &self.layer_path, responses);
@@ -815,10 +828,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} }
} }
} }
NodeGraphMessage::SetSelectedNodes { nodes } => {
responses.add(document.metadata.set_selected_nodes(nodes));
responses.add(PropertiesPanelMessage::ResendActiveProperties);
}
NodeGraphMessage::ShiftNode { node_id } => { NodeGraphMessage::ShiftNode { node_id } => {
let Some(network) = document.document_network.nested_network_mut(&self.network) else { let Some(network) = document.document_network.nested_network_mut(&self.network) else {
warn!("No network"); warn!("No network");
@@ -1,8 +1,8 @@
use super::layer_panel::LayerMetadata; use graph_craft::document::DocumentNode;
use graph_craft::document::NodeId;
use document_legacy::layers::layer_info::Layer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[repr(u8)] #[repr(u8)]
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)] #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, specta::Type)]
@@ -18,6 +18,7 @@ pub const INTERNAL_CLIPBOARD_COUNT: u8 = Clipboard::_InternalClipboardCount as u
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CopyBufferEntry { pub struct CopyBufferEntry {
pub layer: Layer, pub nodes: HashMap<NodeId, DocumentNode>,
pub layer_metadata: LayerMetadata, pub selected: bool,
pub collapsed: bool,
} }
@@ -1,6 +1,7 @@
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId; use document_legacy::LayerId;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graphene_core::text::Font; use graphene_core::text::Font;
@@ -87,13 +88,9 @@ pub enum PortfolioMessage {
document_is_saved: bool, document_is_saved: bool,
document_serialized_content: String, document_serialized_content: String,
}, },
// TODO: Paste message is unused, delete it?
Paste {
clipboard: Clipboard,
},
PasteIntoFolder { PasteIntoFolder {
clipboard: Clipboard, clipboard: Clipboard,
folder_path: Vec<LayerId>, parent: LayerNodeIdentifier,
insert_index: isize, insert_index: isize,
}, },
PasteSerializedData { PasteSerializedData {
@@ -12,7 +12,6 @@ use crate::messages::tool::utility_types::{HintData, HintGroup};
use crate::node_graph_executor::NodeGraphExecutor; use crate::node_graph_executor::NodeGraphExecutor;
use document_legacy::layers::style::RenderData; use document_legacy::layers::style::RenderData;
use document_legacy::Operation as DocumentOperation;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graphene_core::text::Font; use graphene_core::text::Font;
@@ -166,30 +165,49 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
} }
PortfolioMessage::Copy { clipboard } => { PortfolioMessage::Copy { clipboard } => {
// We can't use `self.active_document()` because it counts as an immutable borrow of the entirety of `self` // We can't use `self.active_document()` because it counts as an immutable borrow of the entirety of `self`
if let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get(&id)) { let Some(active_document) = self.active_document_id.and_then(|id| self.documents.get(&id)) else {
let copy_val = |buffer: &mut Vec<CopyBufferEntry>| { return;
for layer_path in active_document.selected_layers_without_children() { };
match (active_document.document_legacy.layer(layer_path).map(|t| t.clone()), *active_document.layer_metadata(layer_path)) {
(Ok(layer), layer_metadata) => {
buffer.push(CopyBufferEntry { layer, layer_metadata });
}
(Err(e), _) => warn!("Could not access selected layer {layer_path:?}: {e:?}"),
}
}
};
if clipboard == Clipboard::Device { let copy_val = |buffer: &mut Vec<CopyBufferEntry>| {
let mut buffer = Vec::new(); for layer_path in active_document.metadata().shallowest_unique_layers(active_document.metadata().selected_layers()) {
copy_val(&mut buffer); let Some(layer) = layer_path.last().copied() else {
let mut copy_text = String::from("graphite/layer: "); continue;
copy_text += &serde_json::to_string(&buffer).expect("Could not serialize paste"); };
responses.add(FrontendMessage::TriggerTextCopy { copy_text }); let node = layer.to_node();
} else { let Some(node) = active_document.network().nodes.get(&node).and_then(|node| node.inputs.first()).and_then(|input| input.as_node()) else {
let copy_buffer = &mut self.copy_buffer; continue;
copy_buffer[clipboard as usize].clear(); };
copy_val(&mut copy_buffer[clipboard as usize]);
buffer.push(CopyBufferEntry {
nodes: NodeGraphMessageHandler::copy_nodes(
active_document.network(),
&active_document
.network()
.all_dependencies(node)
.enumerate()
.map(|(index, (_, node_id))| (node_id, index as NodeId))
.collect(),
)
.collect(),
selected: active_document.metadata().selected_layers_contains(layer),
collapsed: false,
});
} }
};
if clipboard == Clipboard::Device {
let mut buffer = Vec::new();
copy_val(&mut buffer);
let mut copy_text = String::from("graphite/layer: ");
copy_text += &serde_json::to_string(&buffer).expect("Could not serialize paste");
responses.add(FrontendMessage::TriggerTextCopy { copy_text });
} else {
let copy_buffer = &mut self.copy_buffer;
copy_buffer[clipboard as usize].clear();
copy_val(&mut copy_buffer[clipboard as usize]);
} }
} }
PortfolioMessage::Cut { clipboard } => { PortfolioMessage::Cut { clipboard } => {
@@ -364,47 +382,20 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
} }
} }
} }
// TODO: Paste message is unused, delete it? PortfolioMessage::PasteIntoFolder { clipboard, parent, insert_index } => {
PortfolioMessage::Paste { clipboard } => {
let shallowest_common_folder = self.active_document().map(|document| {
document
.document_legacy
.shallowest_common_folder(document.selected_layers())
.expect("While pasting, the selected layers did not exist while attempting to find the appropriate folder path for insertion")
});
if let Some(folder) = shallowest_common_folder {
responses.add(DocumentMessage::DeselectAllLayers);
responses.add(DocumentMessage::StartTransaction);
responses.add(PortfolioMessage::PasteIntoFolder {
clipboard,
folder_path: folder.to_vec(),
insert_index: -1,
});
responses.add(DocumentMessage::CommitTransaction);
}
}
PortfolioMessage::PasteIntoFolder {
clipboard,
folder_path: path,
insert_index,
} => {
let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| { let paste = |entry: &CopyBufferEntry, responses: &mut VecDeque<_>| {
if let Some(document) = self.active_document() { if self.active_document().is_some() {
trace!("Pasting into folder {path:?} as index: {insert_index}"); trace!("Pasting into folder {parent:?} as index: {insert_index}");
let destination_path = [path.to_vec(), vec![generate_uuid()]].concat(); let id = generate_uuid();
responses.add(GraphOperationMessage::NewCustomLayer {
responses.add_front(DocumentMessage::UpdateLayerMetadata { id,
layer_path: destination_path.clone(), nodes: entry.nodes.clone(),
layer_metadata: entry.layer_metadata, parent,
});
document.load_layer_resources(responses);
responses.add_front(DocumentOperation::InsertLayer {
layer: Box::new(entry.layer.clone()),
destination_path,
insert_index, insert_index,
duplicating: false,
}); });
if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
} }
}; };
@@ -421,27 +412,23 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
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 shallowest_common_folder = document let parent = document.metadata().deepest_common_ancestor(document.metadata().selected_layers());
.document_legacy
.shallowest_common_folder(document.selected_layers())
.expect("While pasting from serialized, the selected layers did not exist while attempting to find the appropriate folder path for insertion");
responses.add(DocumentMessage::DeselectAllLayers); responses.add(DocumentMessage::DeselectAllLayers);
responses.add(DocumentMessage::StartTransaction); responses.add(DocumentMessage::StartTransaction);
for entry in data.iter().rev() { for entry in data.into_iter().rev() {
let destination_path = [shallowest_common_folder.to_vec(), vec![generate_uuid()]].concat();
document.load_layer_resources(responses); document.load_layer_resources(responses);
responses.add(DocumentOperation::InsertLayer { let id = generate_uuid();
layer: Box::new(entry.layer.clone()), responses.add(GraphOperationMessage::NewCustomLayer {
destination_path: destination_path.clone(), id,
nodes: entry.nodes,
parent,
insert_index: -1, insert_index: -1,
duplicating: false,
});
responses.add(DocumentMessage::UpdateLayerMetadata {
layer_path: destination_path,
layer_metadata: entry.layer_metadata,
}); });
if entry.selected {
responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
} }
responses.add(DocumentMessage::CommitTransaction); responses.add(DocumentMessage::CommitTransaction);
@@ -557,7 +544,6 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
Import, Import,
NextDocument, NextDocument,
OpenDocument, OpenDocument,
Paste,
PasteIntoFolder, PasteIntoFolder,
PrevDocument, PrevDocument,
); );
@@ -568,7 +554,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
} }
if let Some(document) = self.active_document() { if let Some(document) = self.active_document() {
if document.layer_metadata.values().any(|data| data.selected) { if document.metadata().selected_layers().next().is_some() {
let select = actions!(PortfolioMessageDiscriminant; let select = actions!(PortfolioMessageDiscriminant;
Copy, Copy,
Cut, Cut,
@@ -17,7 +17,7 @@ use std::collections::VecDeque;
pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) { pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
let id = *layer_path.last().unwrap(); let id = *layer_path.last().unwrap();
responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths }); responses.add(GraphOperationMessage::NewVectorLayer { id, subpaths });
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![id] }) responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] })
} }
/// Create a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`] /// Create a new bitmap layer from an [`graphene_core::raster::ImageFrame<Color>`]
@@ -232,7 +232,7 @@ impl<'a> NodeGraphLayer<'a> {
/// Return an iterator up the primary flow of the layer /// Return an iterator up the primary flow of the layer
pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, u64)> { pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, u64)> {
self.node_graph.primary_flow_from_opt(Some(self.layer_node)) self.node_graph.primary_flow_from_node(Some(self.layer_node))
} }
/// Does a node exist in the layer's primary flow /// Does a node exist in the layer's primary flow
@@ -248,9 +248,9 @@ impl PathToolData {
// We didn't find a point nearby, so consider selecting the nearest shape instead // We didn't find a point nearby, so consider selecting the nearest shape instead
else if let Some(layer) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) { else if let Some(layer) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) {
if shift { if shift {
responses.add(NodeGraphMessage::AddSelectNodes { nodes: vec![layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] });
} else { } else {
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
} }
self.drag_start_pos = input.mouse.position; self.drag_start_pos = input.mouse.position;
self.previous_mouse_position = input.mouse.position; self.previous_mouse_position = input.mouse.position;
@@ -386,7 +386,7 @@ impl Fsm for PathToolFsmState {
let shift_pressed = input.keyboard.get(add_to_selection as usize); let shift_pressed = input.keyboard.get(add_to_selection as usize);
if tool_data.drag_start_pos == tool_data.previous_mouse_position { if tool_data.drag_start_pos == tool_data.previous_mouse_position {
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
} else { } else {
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed); shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses); tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
@@ -401,7 +401,7 @@ impl Fsm for PathToolFsmState {
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize); let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
if tool_data.drag_start_pos == tool_data.previous_mouse_position { if tool_data.drag_start_pos == tool_data.previous_mouse_position {
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![] });
} else { } else {
shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed); shape_editor.select_all_in_quad(&document.document_legacy, [tool_data.drag_start_pos, tool_data.previous_mouse_position], !shift_pressed);
tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses); tool_data.refresh_overlays(document, shape_editor, shape_overlay, responses);
@@ -369,7 +369,7 @@ impl SelectToolData {
} }
// Select the originals // Select the originals
responses.add(NodeGraphMessage::SetSelectedNodes { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: originals.iter().map(|layer| layer.to_node()).collect::<Vec<_>>(), nodes: originals.iter().map(|layer| layer.to_node()).collect::<Vec<_>>(),
}); });
@@ -714,13 +714,13 @@ impl Fsm for SelectToolFsmState {
tool_data.layers_dragging.clear(); tool_data.layers_dragging.clear();
tool_data.layers_dragging.extend(replacement_selected_layers.iter()); tool_data.layers_dragging.extend(replacement_selected_layers.iter());
responses.add(NodeGraphMessage::SetSelectedNodes { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: replacement_selected_layers.iter().map(|layer| layer.to_node()).collect(), nodes: replacement_selected_layers.iter().map(|layer| layer.to_node()).collect(),
}); });
} }
} else if let Some(selecting_layer) = tool_data.select_single_layer.take() { } else if let Some(selecting_layer) = tool_data.select_single_layer.take() {
if !tool_data.has_dragged { if !tool_data.has_dragged {
responses.add(NodeGraphMessage::SetSelectedNodes { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: vec![selecting_layer.to_node()], nodes: vec![selecting_layer.to_node()],
}); });
} }
@@ -778,7 +778,7 @@ impl Fsm for SelectToolFsmState {
let quad = tool_data.selection_quad(); let quad = tool_data.selection_quad();
// For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation() // For shallow select we don't update dragging layers until inside drag_start_shallowest_manipulation()
tool_data.layers_dragging = document.metadata().intersect_quad(quad, &document.document_legacy.document_network).collect(); tool_data.layers_dragging = document.metadata().intersect_quad(quad, &document.document_legacy.document_network).collect();
responses.add_front(NodeGraphMessage::SetSelectedNodes { responses.add_front(NodeGraphMessage::SelectedNodesSet {
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(), nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
}); });
responses.add_front(DocumentMessage::Overlays( responses.add_front(DocumentMessage::Overlays(
@@ -906,7 +906,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
let new_selected = ancestor.unwrap_or_else(|| layer.child_of_root(document.metadata())); let new_selected = ancestor.unwrap_or_else(|| layer.child_of_root(document.metadata()));
tool_data.layers_dragging = vec![new_selected]; tool_data.layers_dragging = vec![new_selected];
responses.add(NodeGraphMessage::SetSelectedNodes { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(), nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
}); });
// tool_data // tool_data
@@ -916,7 +916,7 @@ fn drag_shallowest_manipulation(responses: &mut VecDeque<Message>, selected: Vec
fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) { fn drag_deepest_manipulation(responses: &mut VecDeque<Message>, mut selected: Vec<LayerNodeIdentifier>, tool_data: &mut SelectToolData) {
tool_data.layers_dragging.append(&mut selected); tool_data.layers_dragging.append(&mut selected);
responses.add(NodeGraphMessage::SetSelectedNodes { responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(), nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(),
}); });
// tool_data // tool_data
@@ -937,7 +937,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, layer:
return; return;
}; };
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![new_selected.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_selected.to_node()] });
} }
fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, document: &Document, responses: &mut VecDeque<Message>) { fn edit_layer_deepest_manipulation(layer: LayerNodeIdentifier, document: &Document, responses: &mut VecDeque<Message>) {
@@ -269,7 +269,7 @@ impl TextToolData {
self.set_editing(true, render_data, document, responses); self.set_editing(true, render_data, document, responses);
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: vec![self.layer.to_node()] }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
} }
fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState { fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque<Message>) -> TextToolFsmState {
@@ -304,7 +304,7 @@ impl TextToolData {
self.set_editing(true, render_data, document, responses); self.set_editing(true, render_data, document, responses);
responses.add(NodeGraphMessage::SetSelectedNodes { nodes: self.layer.to_path() }); responses.add(NodeGraphMessage::SelectedNodesSet { nodes: self.layer.to_path() });
TextToolFsmState::Editing TextToolFsmState::Editing
} else { } else {
+20 -11
View File
@@ -279,17 +279,6 @@ impl NodeRuntime {
let old_thumbnail = self.thumbnails.entry(node_id).or_default(); let old_thumbnail = self.thumbnails.entry(node_id).or_default();
if *old_thumbnail != render.svg { if *old_thumbnail != render.svg {
responses.add(FrontendMessage::UpdateDocumentLayerDetails {
data: LayerPanelEntry {
name: "Layer".to_string(),
tooltip: format!("Layer id: {node_id}"),
visible: true,
layer_type: LayerDataTypeDiscriminant::Layer,
layer_metadata: LayerMetadata::new(true),
path: vec![node_id],
thumbnail: render.svg.to_string(),
},
});
responses.add(FrontendMessage::UpdateNodeThumbnail { responses.add(FrontendMessage::UpdateNodeThumbnail {
id: node_id, id: node_id,
value: render.svg.to_string(), value: render.svg.to_string(),
@@ -516,6 +505,26 @@ impl NodeGraphExecutor {
new_upstream_transforms, new_upstream_transforms,
transform, transform,
}) => { }) => {
for (&node_id, svg) in &new_thumbnails {
if !document.document_network.nodes.contains_key(&node_id) {
warn!("Missing node");
continue;
}
responses.add(FrontendMessage::UpdateDocumentLayerDetails {
data: LayerPanelEntry {
name: "Layer".to_string(),
tooltip: format!("Layer id: {node_id}"),
visible: true,
layer_type: LayerDataTypeDiscriminant::Layer,
layer_metadata: LayerMetadata {
expanded: true,
selected: document.metadata.selected_layers_contains(LayerNodeIdentifier::new(node_id, &document.document_network)),
},
path: vec![node_id],
thumbnail: svg.to_string(),
},
});
}
self.thumbnails = new_thumbnails; self.thumbnails = new_thumbnails;
document.metadata.update_transforms(new_transforms, new_upstream_transforms); document.metadata.update_transforms(new_transforms, new_upstream_transforms);
document.metadata.update_click_targets(new_click_targets); document.metadata.update_click_targets(new_click_targets);
+5 -6
View File
@@ -5,6 +5,7 @@
use crate::helpers::{translate_key, Error}; use crate::helpers::{translate_key, Error};
use crate::{EDITOR_HAS_CRASHED, EDITOR_INSTANCES, JS_EDITOR_HANDLES}; use crate::{EDITOR_HAS_CRASHED, EDITOR_INSTANCES, JS_EDITOR_HANDLES};
use document_legacy::document_metadata::LayerNodeIdentifier;
use document_legacy::LayerId; use document_legacy::LayerId;
use editor::application::generate_uuid; use editor::application::generate_uuid;
use editor::application::Editor; use editor::application::Editor;
@@ -546,11 +547,9 @@ impl JsEditorHandle {
/// Move a layer to be next to the specified neighbor /// Move a layer to be next to the specified neighbor
#[wasm_bindgen(js_name = moveLayerInTree)] #[wasm_bindgen(js_name = moveLayerInTree)]
pub fn move_layer_in_tree(&self, folder_path: Vec<LayerId>, insert_index: isize) { pub fn move_layer_in_tree(&self, folder_path: Vec<LayerId>, insert_index: isize) {
let message = DocumentMessage::MoveSelectedLayersTo { let parent = folder_path.last().copied().map(LayerNodeIdentifier::new_unchecked).unwrap_or(LayerNodeIdentifier::ROOT);
folder_path,
insert_index, let message = DocumentMessage::MoveSelectedLayersTo { parent, insert_index };
reverse_index: true,
};
self.dispatch(message); self.dispatch(message);
} }
@@ -654,7 +653,7 @@ impl JsEditorHandle {
#[wasm_bindgen(js_name = selectNodes)] #[wasm_bindgen(js_name = selectNodes)]
pub fn select_nodes(&self, nodes: Option<Vec<u64>>) { pub fn select_nodes(&self, nodes: Option<Vec<u64>>) {
let nodes = nodes.unwrap_or_default(); let nodes = nodes.unwrap_or_default();
let message = NodeGraphMessage::SetSelectedNodes { nodes }; let message = NodeGraphMessage::SelectedNodesSet { nodes };
self.dispatch(message); self.dispatch(message);
} }
+17 -8
View File
@@ -616,13 +616,23 @@ impl NodeNetwork {
FlowIter { FlowIter {
stack: self.outputs.iter().map(|output| output.node_id).collect(), stack: self.outputs.iter().map(|output| output.node_id).collect(),
network: self, network: self,
primary: true,
} }
} }
pub fn primary_flow_from_opt(&self, id: Option<NodeId>) -> impl Iterator<Item = (&DocumentNode, u64)> { pub fn primary_flow_from_node(&self, id: Option<NodeId>) -> impl Iterator<Item = (&DocumentNode, u64)> {
FlowIter { FlowIter {
stack: id.map_or_else(|| self.outputs.iter().map(|output| output.node_id).collect(), |id| vec![id]), stack: id.map_or_else(|| self.outputs.iter().map(|output| output.node_id).collect(), |id| vec![id]),
network: self, network: self,
primary: true,
}
}
pub fn all_dependencies(&self, id: NodeId) -> impl Iterator<Item = (&DocumentNode, u64)> {
FlowIter {
stack: vec![id],
network: self,
primary: false,
} }
} }
@@ -664,6 +674,7 @@ impl NodeNetwork {
struct FlowIter<'a> { struct FlowIter<'a> {
stack: Vec<NodeId>, stack: Vec<NodeId>,
network: &'a NodeNetwork, network: &'a NodeNetwork,
primary: bool,
} }
impl<'a> Iterator for FlowIter<'a> { impl<'a> Iterator for FlowIter<'a> {
type Item = (&'a DocumentNode, NodeId); type Item = (&'a DocumentNode, NodeId);
@@ -671,13 +682,11 @@ impl<'a> Iterator for FlowIter<'a> {
loop { loop {
let node_id = self.stack.pop()?; let node_id = self.stack.pop()?;
if let Some(document_node) = self.network.nodes.get(&node_id) { if let Some(document_node) = self.network.nodes.get(&node_id) {
self.stack.extend( let inputs = document_node.inputs.iter().take(if self.primary { 1 } else { usize::MAX });
document_node let node_ids = inputs.filter_map(|input| if let NodeInput::Node { node_id, .. } = input { Some(*node_id) } else { None });
.inputs
.iter() self.stack.extend(node_ids);
.take(1) // Only show the primary input
.filter_map(|input| if let NodeInput::Node { node_id: ref_id, .. } = input { Some(*ref_id) } else { None }),
);
return Some((document_node, node_id)); return Some((document_node, node_id));
}; };
} }