diff --git a/editor/src/messages/dialog/dialog_message_handler.rs b/editor/src/messages/dialog/dialog_message_handler.rs index 96ebf2f0b3..7a3563b769 100644 --- a/editor/src/messages/dialog/dialog_message_handler.rs +++ b/editor/src/messages/dialog/dialog_message_handler.rs @@ -1,5 +1,6 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArtworkDialog, LicensesDialog}; use crate::messages::layout::utility_types::widget_prelude::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::prelude::*; pub struct DialogMessageData<'a> { diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 3d49bc1b6f..48828ee878 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -3,7 +3,7 @@ use super::utility_types::clipboards::Clipboard; use super::utility_types::error::EditorError; use super::utility_types::misc::{SnappingOptions, SnappingState, GET_SNAP_BOX_FUNCTIONS, GET_SNAP_GEOMETRY_FUNCTIONS}; use super::utility_types::network_interface::{NodeNetworkInterface, NodeNetworkPersistentMetadata, TransactionStatus}; -use super::utility_types::nodes::{CollapsedLayers, SelectedNodes}; +use super::utility_types::nodes::{CollapsedLayers, OldSelectedNodes, SelectedNodes}; use crate::application::{generate_uuid, GRAPHITE_GIT_COMMIT_HASH}; use crate::consts::{ASYMPTOTIC_EFFECT, DEFAULT_DOCUMENT_NAME, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL}; use crate::messages::input_mapper::utility_types::macros::action_keys; @@ -48,7 +48,7 @@ pub struct OldDocumentMessageHandler { /// It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content. pub network: OldNodeNetwork, /// List of the [`NodeId`]s that are currently selected by the user. - pub selected_nodes: SelectedNodes, + pub selected_nodes: OldSelectedNodes, /// List of the [`LayerNodeIdentifier`]s that are currently collapsed by the user in the Layers panel. /// Collapsed means that the expansion arrow isn't set to show the children of these layers. pub collapsed: CollapsedLayers, @@ -1507,7 +1507,7 @@ impl DocumentMessageHandler { .unwrap_or_else(|| self.network_interface.all_artboards().iter().next().copied().unwrap_or(LayerNodeIdentifier::ROOT_PARENT)) } - pub fn get_calculated_insert_index(metadata: &DocumentMetadata, selected_nodes: SelectedNodes, parent: LayerNodeIdentifier) -> usize { + pub fn get_calculated_insert_index(metadata: &DocumentMetadata, selected_nodes: impl SelectedNodes, parent: LayerNodeIdentifier) -> usize { parent .children(metadata) .enumerate() diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index ad943ebb56..3cc7e94c98 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -7,6 +7,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions: use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType}; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{self, NodeNetworkInterface, NodeTemplate}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry}; use crate::messages::prelude::*; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; @@ -1037,27 +1038,15 @@ impl<'a> MessageHandler> for NodeGrap responses.add(PortfolioMessage::SubmitGraphRender { document_id, ignore_hash: true }); } NodeGraphMessage::SelectedNodesAdd { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesAdd"); - return; - }; - selected_nodes.add_selected_nodes(nodes); + network_interface.add_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesRemove { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesRemove"); - return; - }; - selected_nodes.retain_selected_nodes(|node| !nodes.contains(node)); + network_interface.remove_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); } NodeGraphMessage::SelectedNodesSet { nodes } => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::SelectedNodesSet"); - return; - }; - selected_nodes.set_selected_nodes(nodes); + network_interface.set_selected_nodes(nodes, selection_network_path); responses.add(BroadcastEvent::SelectionChanged); responses.add(PropertiesPanelMessage::Refresh); } @@ -1324,11 +1313,7 @@ impl<'a> MessageHandler> for NodeGrap // Update the import/export UI edges whenever the PTZ changes or the bounding box of all nodes changes } NodeGraphMessage::UpdateNewNodeGraph => { - let Some(selected_nodes) = network_interface.selected_nodes_mut(selection_network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::UpdateNewNodeGraph"); - return; - }; - selected_nodes.clear_selected_nodes(); + network_interface.set_selected_nodes(Vec::new(), selection_network_path); responses.add(BroadcastEvent::SelectionChanged); responses.add(NodeGraphMessage::SendGraph); @@ -1343,6 +1328,7 @@ impl<'a> MessageHandler> for NodeGrap for path in resolved_types.remove { network_interface.resolved_types.types.remove(&path.to_vec()); } + self.node_graph_errors = node_graph_errors; } NodeGraphMessage::UpdateActionButtons => { diff --git a/editor/src/messages/portfolio/document/overlays/utility_functions.rs b/editor/src/messages/portfolio/document/overlays/utility_functions.rs index c99d83e3b4..c8554111c9 100644 --- a/editor/src/messages/portfolio/document/overlays/utility_functions.rs +++ b/editor/src/messages/portfolio/document/overlays/utility_functions.rs @@ -2,6 +2,7 @@ use super::utility_types::OverlayContext; use crate::consts::HIDE_HANDLE_DISTANCE; use crate::messages::tool::common_functionality::shape_editor::{SelectedLayerState, ShapeState}; use crate::messages::tool::tool_messages::tool_prelude::DocumentMessageHandler; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::ManipulatorPointId; diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 03edf9d606..93139bab30 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -127,20 +127,22 @@ impl NodeNetworkInterface { } /// Get the selected nodes for the network at the network_path - pub fn selected_nodes(&self, network_path: &[NodeId]) -> Option { - let Some(network_metadata) = self.network_metadata(network_path) else { - log::error!("Could not get nested network_metadata in selected_nodes"); + pub fn selected_nodes(&self, network_path: &[NodeId]) -> Option { + let Some(network) = self.network(network_path) else { + log::error!("Could not get network in selected_nodes"); + return None; + }; + let Some(selection_undo_history) = self.selection_undo_history(network_path) else { + log::error!("Could not get nested selection_undo_history for path {network_path:?}"); return None; }; Some( - network_metadata - .persistent_metadata - .selection_undo_history + selection_undo_history .back() .cloned() .unwrap_or_default() - .filtered_selected_nodes(network_metadata.persistent_metadata.node_metadata.keys().cloned().collect()), + .filtered_selected_nodes(network.nodes.keys().cloned().collect()), ) } @@ -1079,6 +1081,18 @@ impl NodeNetworkInterface { Some(top_right) } + pub fn selection_undo_history(&self, network_path: &[NodeId]) -> Option<&VecDeque>> { + let Some(tagged_value) = self.metadata_value(MetadataType::SelectionUndoHistory, network_path) else { + log::error!("Could not get tagged value in selection_undo_history"); + return None; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = tagged_value else { + log::error!("Tagged value should be SelectionUndoHistory in selection_undo_history"); + return None; + }; + Some(selection_undo_history) + } + pub fn reference(&self, node_id: &NodeId, network_path: &[NodeId]) -> Option { self.node_metadata(node_id, network_path) .and_then(|node_metadata| node_metadata.persistent_metadata.reference.as_ref().map(|reference| reference.to_string())) @@ -1459,7 +1473,6 @@ impl NodeNetworkInterface { log::error!("Could not get nested network in from_old_network"); continue; }; - nested_network_metadata.persistent_metadata.previewing = Previewing::No; for (node_id, old_node) in old_network.nodes { let mut node = DocumentNode::default(); let mut node_metadata = DocumentNodeMetadata::default(); @@ -1613,44 +1626,137 @@ impl NodeNetworkInterface { self.transaction_status = TransactionStatus::Finished; } - /// Mutably get the selected nodes for the network at the network_path. Every time they are mutated, the transient metadata for the top of the stack gets unloaded. - pub fn selected_nodes_mut(&mut self, network_path: &[NodeId]) -> Option<&mut SelectedNodes> { - self.unload_stack_dependents(network_path); - let Some(network_metadata) = self.network_metadata_mut(network_path) else { + /// Change the selected nodes for the network at the network_path. Every time they are mutated, the transient metadata for the top of the stack gets unloaded. + fn mutate_selected_nodes(&mut self, nodes: Vec, selection_operation: SelectionOperation, network_path: &[NodeId]) { + let Some(selection_undo_history) = self.selection_undo_history(network_path) else { log::error!("Could not get nested network_metadata in selected_nodes"); - return None; + return; }; - let last_selection_state = network_metadata.persistent_metadata.selection_undo_history.back().cloned().unwrap_or_default(); - - network_metadata.persistent_metadata.selection_undo_history.push_back(last_selection_state); - network_metadata.persistent_metadata.selection_redo_history.clear(); - - if network_metadata.persistent_metadata.selection_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { - network_metadata.persistent_metadata.selection_undo_history.pop_front(); + let mut last_selection_state = selection_undo_history.back().cloned().unwrap_or_default(); + match selection_operation { + SelectionOperation::Add => { + last_selection_state.extend(nodes); + } + SelectionOperation::Remove => { + last_selection_state.retain(|node| !nodes.contains(node)); + } + SelectionOperation::Set => { + last_selection_state = nodes; + } } - network_metadata.persistent_metadata.selection_undo_history.back_mut() + + let network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_path); + let Some(metadata_node) = network.nodes.get_mut(&metadata_node_id) else { + log::error!("Could not get metadata node with id {metadata_node_id} in set_metadata"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in set_metadata"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() else { + log::error!("Could not get tagged value in set_metadata"); + return; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in set_metadata"); + return; + }; + selection_undo_history.push_back(last_selection_state); + if selection_undo_history.len() > crate::consts::MAX_UNDO_HISTORY_LEN { + selection_undo_history.pop_front(); + } + drop(value); + self.unload_stack_dependents(network_path); + self.set_metadata(MetadataType::SelectionRedoHistory, TaggedValue::SelectionHistory(VecDeque::new()), network_path); + } + + pub fn add_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Add, network_path); + } + pub fn remove_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Remove, network_path); + } + pub fn set_selected_nodes(&mut self, nodes: Vec, network_path: &[NodeId]) { + self.mutate_selected_nodes(nodes, SelectionOperation::Set, network_path); + } + pub fn remove_selection_history_step(&mut self, network_path: &[NodeId]) { + let network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(MetadataType::SelectionUndoHistory, network_path); + let Some(metadata_node) = network.nodes.get_mut(&metadata_node_id) else { + log::error!("Could not get metadata node with id {metadata_node_id} in set_metadata"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in set_metadata"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() else { + log::error!("Could not get tagged value in set_metadata"); + return; + }; + let TaggedValue::SelectionHistory(selection_undo_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionUndoHistory in set_metadata"); + return; + }; + selection_undo_history.pop_back(); } pub fn selection_step_back(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in selection_step_back"); - return; - }; - - if let Some(selection_state) = network_metadata.persistent_metadata.selection_undo_history.pop_back() { - network_metadata.persistent_metadata.selection_redo_history.push_front(selection_state); - } + self.selection_step(SelectionDirection::Back, network_path); } pub fn selection_step_forward(&mut self, network_path: &[NodeId]) { - let Some(network_metadata) = self.network_metadata_mut(network_path) else { - log::error!("Could not get nested network_metadata in selection_step_forward"); - return; + self.selection_step(SelectionDirection::Forward, network_path); + } + + fn selection_step(&mut self, direction: SelectionDirection, network_path: &[NodeId]) { + let (adding_to, removing_from) = match direction { + SelectionDirection::Back => (MetadataType::SelectionUndoHistory, MetadataType::SelectionRedoHistory), + SelectionDirection::Forward => (MetadataType::SelectionRedoHistory, MetadataType::SelectionUndoHistory), }; - if let Some(selection_state) = network_metadata.persistent_metadata.selection_redo_history.pop_front() { - network_metadata.persistent_metadata.selection_undo_history.push_back(selection_state); + let network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(adding_to, network_path); + let Some(metadata_node) = network.nodes.get_mut(&metadata_node_id) else { + log::error!("Could not get metadata node with id {metadata_node_id} in selection_step"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in selection_step"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() else { + log::error!("Could not get tagged value in selection_step"); + return; + }; + let TaggedValue::SelectionHistory(selection_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionHistory in selection_step"); + return; + }; + if let Some(selection_state) = selection_history.pop_back() { + drop(value); + let network = self.network_mut(&[]).unwrap(); + let metadata_node_id = Self::metadata_node_id(removing_from, network_path); + let Some(metadata_node) = network.nodes.get_mut(&metadata_node_id) else { + log::error!("Could not get metadata node with id {metadata_node_id} in selection_step"); + return; + }; + let Some(metadata_input) = metadata_node.inputs.get_mut(0) else { + log::error!("Could not get metadata input in selection_step"); + return; + }; + let Some(mut value) = metadata_input.as_value_mut() else { + log::error!("Could not get tagged value in selection_step"); + return; + }; + let TaggedValue::SelectionHistory(selection_history) = value.deref_mut() else { + log::error!("Tagged value should be SelectionHistory in selection_step"); + return; + }; + selection_history.push_back(selection_state); } } @@ -3519,6 +3625,16 @@ impl NodeNetworkInterface { TaggedValue::DVec2(persistent_metadata.navigation_metadata.node_graph_top_right), network_path, ); + self.insert_node_metadata( + MetadataType::SelectionUndoHistory, + TaggedValue::SelectionHistory(persistent_metadata.selection_undo_history), + network_path, + ); + self.insert_node_metadata( + MetadataType::SelectionRedoHistory, + TaggedValue::SelectionHistory(persistent_metadata.selection_redo_history), + network_path, + ); // TODO: Add the rest of the network metadata nodes for (node_id, node_metadata) in persistent_metadata.node_metadata { @@ -3635,11 +3751,7 @@ impl NodeNetworkInterface { self.unload_all_nodes_bounding_box(network_path); // Instead of unloaded all node click targets, just unload the nodes upstream from the deleted nodes. unload_upstream_node_click_targets will not work since the nodes have been deleted. self.unload_all_nodes_click_targets(network_path); - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in NodeGraphMessage::DeleteNodes"); - return; - }; - selected_nodes.retain_selected_nodes(|node_id| !nodes_to_delete.contains(node_id)); + self.remove_selected_nodes(nodes_to_delete, network_path); } /// Removes all references to the node with the given id from the network, and reconnects the input to the node below. @@ -3703,12 +3815,7 @@ impl NodeNetworkInterface { let upstream_nodes = self.upstream_flow_back_from_nodes(vec![*reconnect_node], network_path, FlowType::PrimaryFlow).collect::>(); // Select the reconnect node to move to ensure the shifting works correctly - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in remove_references_from_network"); - return false; - }; - - let old_selected_nodes = selected_nodes.replace_with(upstream_nodes); + self.set_selected_nodes(upstream_nodes, network_path); // Shift up until there is either a collision or the disconnected node position is reached let mut current_shift_distance = 0; @@ -3717,7 +3824,7 @@ impl NodeNetworkInterface { current_shift_distance += 1; } - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } true @@ -4774,11 +4881,7 @@ impl NodeNetworkInterface { // If there is an upstream node in the new location for the layer, create space for the moved layer by shifting the upstream node down if let Some(upstream_node_id) = post_node_input.as_node() { // Select the layer to move to ensure the shifting works correctly - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in move_layer_to_stack"); - return; - }; - let old_selected_nodes = selected_nodes.replace_with(vec![upstream_node_id]); + self.set_selected_nodes(vec![upstream_node_id], network_path); // Create the minimum amount space for the moved layer for _ in 0..3 { @@ -4797,7 +4900,7 @@ impl NodeNetworkInterface { self.vertical_shift_with_push(&upstream_node_id, 1, &mut HashSet::new(), network_path); } - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } // If inserting into a stack with a parent, ensure the parent stack has enough space for the child stack @@ -4836,17 +4939,13 @@ impl NodeNetworkInterface { let upstream_nodes = self .upstream_flow_back_from_nodes(vec![upstream_sibling.to_node()], network_path, FlowType::UpstreamFlow) .collect::>(); - let Some(selected_nodes) = self.selected_nodes_mut(network_path) else { - log::error!("Could not get selected nodes in move_layer_to_stack"); - return; - }; - let old_selected_nodes = selected_nodes.replace_with(upstream_nodes); + + self.set_selected_nodes(upstream_nodes, network_path); for _ in 0..(target_gap - current_gap).max(0) { self.shift_selected_nodes(Direction::Down, true, network_path); } - - let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes); + self.remove_selection_history_step(network_path); } } @@ -5131,10 +5230,10 @@ pub struct NodeNetworkPersistentMetadata { pub navigation_metadata: NavigationMetadata, /// Stack of selection snapshots for previous history states. #[serde(default)] - pub selection_undo_history: VecDeque, + pub selection_undo_history: VecDeque>, /// Stack of selection snapshots for future history states. #[serde(default)] - pub selection_redo_history: VecDeque, + pub selection_redo_history: VecDeque>, } /// This is the same as Option, but more clear in the context of having cached metadata either being loaded or unloaded @@ -5159,7 +5258,6 @@ impl TransientMetadata { /// If some network calculation is too slow to compute for every usage, cache the data here #[derive(Debug, Default, Clone)] pub struct NodeNetworkTransientMetadata { - pub selected_nodes: SelectedNodes, /// Sole dependents of the top of the stacks of all selected nodes. Used to determine which nodes are checked for collision when shifting. /// The LayerOwner is used to determine whether the collided node should be shifted, or the layer that owns it. pub stack_dependents: TransientMetadata>, @@ -5422,6 +5520,17 @@ impl Default for NavigationMetadata { } } +enum SelectionDirection { + Back, + Forward, +} + +enum SelectionOperation { + Add, + Remove, + Set, +} + // PartialEq required by message handlers /// All persistent editor and Graphene data for a node. Used to serialize and deserialize a node, pass it through the editor, and create definitions. #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] diff --git a/editor/src/messages/portfolio/document/utility_types/nodes.rs b/editor/src/messages/portfolio/document/utility_types/nodes.rs index de101fb56b..523418556e 100644 --- a/editor/src/messages/portfolio/document/utility_types/nodes.rs +++ b/editor/src/messages/portfolio/document/utility_types/nodes.rs @@ -58,10 +58,47 @@ pub struct LayerPanelEntry { } #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)] -pub struct SelectedNodes(pub Vec); +pub struct OldSelectedNodes(pub Vec); -impl SelectedNodes { - pub fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { +pub trait SelectedNodes { + fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool; + + fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool; + + fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_; + + fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_; + + fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool; + + fn selected_nodes(&self) -> impl Iterator + '_; + + fn selected_nodes_ref(&self) -> &Vec; + + fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool; + + fn has_selected_nodes(&self) -> bool; + + fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool); + + fn set_selected_nodes(&mut self, new: Vec); + + fn add_selected_nodes(&mut self, new: Vec); + + fn clear_selected_nodes(&mut self); + + fn replace_with(&mut self, new: Vec) -> Vec; + + fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> Vec; +} +impl SelectedNodes for Vec { + fn layer_visible(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { layer.ancestors(network_interface.document_metadata()).all(|layer| { if layer != LayerNodeIdentifier::ROOT_PARENT { network_interface.is_visible(&layer.to_node(), &[]) @@ -71,12 +108,12 @@ impl SelectedNodes { }) } - pub fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_visible_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| self.layer_visible(layer, network_interface)) } - pub fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { + fn layer_locked(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> bool { layer.ancestors(network_interface.document_metadata()).any(|layer| { if layer != LayerNodeIdentifier::ROOT_PARENT { network_interface.is_locked(&layer.to_node(), &[]) @@ -86,67 +123,67 @@ impl SelectedNodes { }) } - pub fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| !self.layer_locked(layer, network_interface)) } - pub fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_visible_and_unlocked_layers<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| self.layer_visible(layer, network_interface) && !self.layer_locked(layer, network_interface)) } - pub fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { - metadata.all_layers().filter(|layer| self.0.contains(&layer.to_node())) + fn selected_layers<'a>(&'a self, metadata: &'a DocumentMetadata) -> impl Iterator + '_ { + metadata.all_layers().filter(|layer| self.contains(&layer.to_node())) } - pub fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { + fn selected_layers_except_artboards<'a>(&'a self, network_interface: &'a NodeNetworkInterface) -> impl Iterator + '_ { self.selected_layers(network_interface.document_metadata()) .filter(move |&layer| !network_interface.is_artboard(&layer.to_node(), &[])) } - pub fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool { + fn selected_layers_contains(&self, layer: LayerNodeIdentifier, metadata: &DocumentMetadata) -> bool { self.selected_layers(metadata).any(|selected| selected == layer) } - pub fn selected_nodes(&self) -> impl Iterator + '_ { - self.0.iter() + fn selected_nodes(&self) -> impl Iterator + '_ { + self.iter() } - pub fn selected_nodes_ref(&self) -> &Vec { - &self.0 + fn selected_nodes_ref(&self) -> &Vec { + &self } - pub fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool { - self.0.iter().any(|node_id| network.nodes.contains_key(node_id)) + fn network_has_selected_nodes(&self, network: &NodeNetwork) -> bool { + self.iter().any(|node_id| network.nodes.contains_key(node_id)) } - pub fn has_selected_nodes(&self) -> bool { - !self.0.is_empty() + fn has_selected_nodes(&self) -> bool { + !self.is_empty() } - pub fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) { - self.0.retain(f); + fn retain_selected_nodes(&mut self, f: impl FnMut(&NodeId) -> bool) { + self.retain(f); } - pub fn set_selected_nodes(&mut self, new: Vec) { - self.0 = new; + fn set_selected_nodes(&mut self, new: Vec) { + *self = new; } - pub fn add_selected_nodes(&mut self, new: Vec) { - self.0.extend(new); + fn add_selected_nodes(&mut self, new: Vec) { + self.extend(new); } - pub fn clear_selected_nodes(&mut self) { - self.0 = Vec::new(); + fn clear_selected_nodes(&mut self) { + *self = Vec::new(); } - pub fn replace_with(&mut self, new: Vec) -> Vec { - std::mem::replace(&mut self.0, new) + fn replace_with(&mut self, new: Vec) -> Vec { + std::mem::replace(self, new) } - pub fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> SelectedNodes { - SelectedNodes(self.0.iter().filter(|node_id| node_ids.contains(node_id)).cloned().collect()) + fn filtered_selected_nodes(&self, node_ids: std::collections::HashSet) -> Vec { + self.iter().filter(|node_id| node_ids.contains(node_id)).cloned().collect() } } diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index c9d628e410..a1f5ecd95e 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -7,6 +7,7 @@ use crate::messages::dialog::simple_dialogs; use crate::messages::frontend::utility_types::FrontendDocumentDetails; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::DocumentMessageData; use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup}; diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index c56f12127e..d408788a24 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -6,6 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::prelude::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use glam::{DAffine2, DVec2}; use std::collections::VecDeque; diff --git a/editor/src/messages/tool/common_functionality/utility_functions.rs b/editor/src/messages/tool/common_functionality/utility_functions.rs index 21aed42521..1aad2ce938 100644 --- a/editor/src/messages/tool/common_functionality/utility_functions.rs +++ b/editor/src/messages/tool/common_functionality/utility_functions.rs @@ -1,4 +1,5 @@ use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::prelude::*; use graphene_std::vector::PointId; diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 263a1bd416..25139f65a8 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -8,6 +8,7 @@ use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint; use crate::messages::tool::common_functionality::snapping::SnapData; use crate::messages::tool::common_functionality::snapping::SnapManager; use crate::messages::tool::common_functionality::transformation_cage::*; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graph_craft::document::NodeId; use graphene_core::renderer::Quad; diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index 5b8d733f46..1ce9fd2f3f 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -4,6 +4,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions: use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::FlowType; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graph_craft::document::value::TaggedValue; use graph_craft::document::NodeId; diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index e90f60511e..eed6b10b13 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -5,6 +5,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::graph_modification_utils::get_gradient; use crate::messages::tool::common_functionality::snapping::SnapManager; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::style::{Fill, Gradient, GradientType}; diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index c27114307b..e2b127bdf2 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -7,6 +7,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::shape_editor::{ClosestSegment, ManipulatorAngle, OpposingHandleLengths, SelectedPointsInfo, ShapeState}; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::renderer::Quad; use graphene_core::vector::ManipulatorPointId; diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 2a836421e0..3e9fc4fcb5 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -9,6 +9,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis}; use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, NodeNetworkInterface, NodeTemplate}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::portfolio::document::utility_types::transformation::Selected; use crate::messages::tool::common_functionality::graph_modification_utils::is_layer_fed_by_node_of_name; use crate::messages::tool::common_functionality::pivot::Pivot; diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index 41a56c3aec..0b5158a487 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -5,6 +5,7 @@ use crate::application::generate_uuid; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, is_layer_fed_by_node_of_name}; diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index a7a38c81ef..11d66397b7 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::transformation::{Axis, use crate::messages::prelude::*; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::utility_types::{ToolData, ToolType}; +use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes; use graphene_core::vector::ManipulatorPointId; diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 44f86c6eb8..d392f7235a 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -11,6 +11,7 @@ use graphene_core::{Color, MemoHash, Node, Type}; use dyn_any::DynAny; pub use dyn_any::StaticType; pub use glam::{DAffine2, DVec2, IVec2, UVec2}; +use std::collections::VecDeque; use std::fmt::Display; use std::hash::Hash; use std::marker::PhantomData; @@ -180,6 +181,7 @@ tagged_value! { FontCache(Arc), Previewing(crate::document::Previewing), PTZ(crate::document::PTZ), + SelectionHistory(VecDeque>), } impl TaggedValue {