diff --git a/document-legacy/src/document.rs b/document-legacy/src/document.rs index 4d522c85..e388bb70 100644 --- a/document-legacy/src/document.rs +++ b/document-legacy/src/document.rs @@ -1,4 +1,4 @@ -use crate::document_metadata::DocumentMetadata; +use crate::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier}; use crate::intersection::Quad; use crate::layers::folder_layer::FolderLayer; use crate::layers::layer_info::{Layer, LayerData, LayerDataType, LayerDataTypeDiscriminant}; @@ -6,10 +6,13 @@ use crate::layers::layer_layer::{CachedOutputData, LayerLayer}; use crate::layers::shape_layer::ShapeLayer; use crate::layers::style::RenderData; use crate::{DocumentError, DocumentResponse, Operation}; +use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeNetwork, NodeOutput}; +use graphene_core::renderer::ClickTarget; +use graphene_core::transform::Footprint; +use graphene_core::{concrete, generic, NodeIdentifier}; +use graphene_std::wasm_application_io::WasmEditorApi; use glam::{DAffine2, DVec2}; -use graphene_core::transform::Footprint; -use graphene_std::wasm_application_io::WasmEditorApi; use serde::{Deserialize, Serialize}; use std::cmp::max; use std::collections::hash_map::DefaultHasher; @@ -17,10 +20,6 @@ use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::vec; -use graph_craft::document::{DocumentNode, NodeOutput}; -use graph_craft::document::{DocumentNodeImplementation, NodeId}; -use graphene_core::{concrete, generic, NodeIdentifier}; - /// A number that identifies a layer. /// This does not technically need to be unique globally, only within a folder. pub type LayerId = u64; @@ -34,7 +33,9 @@ pub struct Document { #[serde(skip)] pub state_identifier: DefaultHasher, #[serde(default)] - pub document_network: graph_craft::document::NodeNetwork, + pub document_network: NodeNetwork, + #[serde(default)] + pub collapsed_folders: Vec, #[serde(skip)] pub metadata: DocumentMetadata, #[serde(default)] @@ -53,7 +54,7 @@ impl Default for Document { root: Layer::new(LayerDataType::Folder(FolderLayer::default()), DAffine2::IDENTITY.to_cols_array()), state_identifier: DefaultHasher::new(), document_network: { - use graph_craft::document::{value::TaggedValue, NodeInput, NodeNetwork}; + use graph_craft::document::{value::TaggedValue, NodeInput}; let mut network = NodeNetwork::default(); let node = graph_craft::document::DocumentNode { name: "Output".into(), @@ -106,12 +107,61 @@ impl Default for Document { network }, metadata: Default::default(), + collapsed_folders: Vec::new(), commit_hash: String::new(), } } } impl Document { + pub fn layer_visible(&self, layer: LayerNodeIdentifier) -> bool { + !layer.ancestors(&self.metadata).any(|layer| self.document_network.disabled.contains(&layer.to_node())) + } + + pub fn selected_visible_layers(&self) -> impl Iterator + '_ { + self.metadata.selected_layers().filter(|&layer| self.layer_visible(layer)) + } + + pub fn load_network_structure(&mut self) { + self.metadata.load_structure(&self.document_network); + self.collapsed_folders.retain(|&layer| self.metadata.layer_exists(layer)); + } + + /// Runs an intersection test with all layers and a viewport space quad + pub fn intersect_quad<'a>(&'a self, viewport_quad: graphene_core::renderer::Quad, network: &'a NodeNetwork) -> impl Iterator + 'a { + let document_quad = self.metadata.document_to_viewport.inverse() * viewport_quad; + self.metadata + .root() + .decendants(&self.metadata) + .filter(|&layer| self.layer_visible(layer)) + .filter(|&layer| !is_artboard(layer, network)) + .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) + .filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.metadata.transform_to_document(*layer)))) + .map(|(layer, _)| layer) + } + + /// Find all of the layers that were clicked on from a viewport space location + pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator + '_ { + let point = self.metadata.document_to_viewport.inverse().transform_point2(viewport_location); + self.metadata + .root() + .decendants(&self.metadata) + .filter(|&layer| self.layer_visible(layer)) + .filter_map(|layer| self.metadata.click_target(layer).map(|targets| (layer, targets))) + .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.metadata.transform_to_document(*layer)))) + .map(|(layer, _)| layer) + } + + /// Find the layer that has been clicked on from a viewport space location + pub fn click(&self, viewport_location: DVec2, network: &NodeNetwork) -> Option { + self.click_xray(viewport_location).find(|&layer| !is_artboard(layer, network)) + } + pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> { + self.selected_visible_layers() + .filter_map(|layer| self.metadata.bounding_box_viewport(layer)) + .reduce(graphene_core::renderer::Quad::combine_bounds) + } + /// Wrapper around render, that returns the whole document as a Response. pub fn render_root(&mut self, render_data: &RenderData) -> String { // Render and append to the defs section diff --git a/document-legacy/src/document_metadata.rs b/document-legacy/src/document_metadata.rs index 9bc7a91a..dfe8a4f5 100644 --- a/document-legacy/src/document_metadata.rs +++ b/document-legacy/src/document_metadata.rs @@ -55,10 +55,6 @@ impl DocumentMetadata { self.selected_layers().any(|selected| selected == layer) } - pub fn selected_visible_layers(&self) -> impl Iterator + '_ { - self.selected_layers() - } - pub fn selected_nodes(&self) -> core::slice::Iter<'_, NodeId> { self.selected_nodes.iter() } @@ -71,6 +67,14 @@ impl DocumentMetadata { !self.selected_nodes.is_empty() } + pub fn layer_exists(&self, layer: LayerNodeIdentifier) -> bool { + self.structure.contains_key(&layer) + } + + pub fn click_target(&self, layer: LayerNodeIdentifier) -> Option<&Vec> { + self.click_targets.get(&layer) + } + /// Access the [`NodeRelations`] of a layer. fn get_relations(&self, node_identifier: LayerNodeIdentifier) -> Option<&NodeRelations> { self.structure.get(&node_identifier) @@ -97,13 +101,13 @@ impl DocumentMetadata { } /// Ancestor that is shared by all layers and that is deepest (more nested). Default may be the root. - pub fn deepest_common_ancestor(&self, layers: impl Iterator) -> Option { + pub fn deepest_common_ancestor(&self, layers: impl Iterator, include_self: bool) -> Option { layers .map(|layer| { let mut layer_path = layer.ancestors(self).collect::>(); layer_path.reverse(); - if !self.folders.contains(&layer) { + if include_self || !self.folders.contains(&layer) { layer_path.pop(); } @@ -200,6 +204,11 @@ impl DocumentMetadata { current = sibling_below(graph, current_node); } } + + self.selected_nodes.retain(|node| graph.nodes.contains_key(node)); + self.upstream_transforms.retain(|node, _| graph.nodes.contains_key(node)); + self.transforms.retain(|layer, _| self.structure.contains_key(layer)); + self.click_targets.retain(|layer, _| self.structure.contains_key(layer)); } } @@ -235,11 +244,11 @@ impl DocumentMetadata { } } -fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool { +pub fn is_artboard(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool { network.primary_flow_from_node(Some(layer.to_node())).any(|(node, _)| node.name == "Artboard") } -fn is_folder(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool { +pub fn is_folder(layer: LayerNodeIdentifier, network: &NodeNetwork) -> bool { network.nodes.get(&layer.to_node()).and_then(|node| node.inputs.first()).is_some_and(|input| input.as_node().is_none()) || network .primary_flow_from_node(Some(layer.to_node())) @@ -254,32 +263,6 @@ impl DocumentMetadata { self.click_targets = new_click_targets; } - /// Runs an intersection test with all layers and a viewport space quad - pub fn intersect_quad<'a>(&'a self, viewport_quad: Quad, network: &'a NodeNetwork) -> impl Iterator + 'a { - let document_quad = self.document_to_viewport.inverse() * viewport_quad; - self.root() - .decendants(self) - .filter(|&layer| !is_artboard(layer, network)) - .filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets))) - .filter(move |(layer, target)| target.iter().any(move |target| target.intersect_rectangle(document_quad, self.transform_to_document(*layer)))) - .map(|(layer, _)| layer) - } - - /// Find all of the layers that were clicked on from a viewport space location - pub fn click_xray(&self, viewport_location: DVec2) -> impl Iterator + '_ { - let point = self.document_to_viewport.inverse().transform_point2(viewport_location); - self.root() - .decendants(self) - .filter_map(|layer| self.click_targets.get(&layer).map(|targets| (layer, targets))) - .filter(move |(layer, target)| target.iter().any(|target: &ClickTarget| target.intersect_point(point, self.transform_to_document(*layer)))) - .map(|(layer, _)| layer) - } - - /// Find the layer that has been clicked on from a viewport space location - pub fn click(&self, viewport_location: DVec2, network: &NodeNetwork) -> Option { - self.click_xray(viewport_location).find(|&layer| !is_artboard(layer, network)) - } - /// Get the bounding box of the click target of the specified layer in the specified transform space pub fn bounding_box_with_transform(&self, layer: LayerNodeIdentifier, transform: DAffine2) -> Option<[DVec2; 2]> { self.click_targets @@ -316,10 +299,6 @@ impl DocumentMetadata { self.bounding_box_with_transform(layer, self.transform_to_viewport(layer)) } - pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> { - self.selected_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds) - } - /// Calculates the document bounds used for scrolling and centring (the layer bounds or the artboard (if applicable)) pub fn document_bounds(&self) -> Option<[DVec2; 2]> { self.all_layers().filter_map(|layer| self.bounding_box_viewport(layer)).reduce(Quad::combine_bounds) diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/default_mapping.rs index cf4d070e..2f197319 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/default_mapping.rs @@ -52,7 +52,7 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut), entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy), entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes), - entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleHidden), + entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedHidden), // // TransformLayerMessage entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation), diff --git a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs index d4f3308e..5fe7ce8e 100644 --- a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs +++ b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs @@ -202,6 +202,7 @@ pub enum Key { // Other keys that aren't part of the W3C spec Command, + /// "Ctrl" on Windows/Linux, "Cmd" on Mac Accel, Lmb, Rmb, diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 499db122..a23657ce 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -185,10 +185,7 @@ pub enum DocumentMessage { }, StartTransaction, ToggleLayerExpansion { - layer_path: Vec, - }, - ToggleLayerVisibility { - layer_path: Vec, + layer: NodeId, }, Undo, UndoFinished, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 660ee85e..1673b1b0 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -162,7 +162,7 @@ impl MessageHandler> for DocumentMessageHand self.navigation_handler.process_message( message, responses, - (&self.document_legacy, document_bounds, ipp, self.metadata().selected_visible_layers_bounding_box_viewport()), + (&self.document_legacy, document_bounds, ipp, self.document_legacy.selected_visible_layers_bounding_box_viewport()), ); } #[remain::unsorted] @@ -223,7 +223,7 @@ impl MessageHandler> for DocumentMessageHand AlignAxis::X => DVec2::X, AlignAxis::Y => DVec2::Y, }; - let Some(combined_box) = self.metadata().selected_visible_layers_bounding_box_viewport() else { + let Some(combined_box) = self.document_legacy.selected_visible_layers_bounding_box_viewport() else { return; }; @@ -276,13 +276,13 @@ impl MessageHandler> for DocumentMessageHand CreateEmptyFolder { parent } => { let id = generate_uuid(); - responses.add(DocumentMessage::DeselectAllLayers); responses.add(GraphOperationMessage::NewCustomLayer { id, nodes: HashMap::new(), parent, insert_index: -1, }); + responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] }); } DebugPrintDocument => { info!("{:#?}\n{:#?}", self.document_legacy, self.layer_metadata); @@ -356,7 +356,7 @@ impl MessageHandler> for DocumentMessageHand // Calculate the bounding box of the region to be exported let bounds = match bounds { ExportBounds::AllArtwork => self.all_layer_bounds(&render_data), - ExportBounds::Selection => self.metadata().selected_visible_layers_bounding_box_viewport(), + ExportBounds::Selection => self.document_legacy.selected_visible_layers_bounding_box_viewport(), ExportBounds::Artboard(id) => self.metadata().bounding_box_document(id), } .unwrap_or_default(); @@ -387,7 +387,7 @@ impl MessageHandler> for DocumentMessageHand FlipAxis::X => DVec2::new(-1., 1.), FlipAxis::Y => DVec2::new(1., -1.), }; - if let Some([min, max]) = self.metadata().selected_visible_layers_bounding_box_viewport() { + if let Some([min, max]) = self.document_legacy.selected_visible_layers_bounding_box_viewport() { let center = (max + min) / 2.; let bbox_trans = DAffine2::from_translation(-center); for layer in self.metadata().selected_layers() { @@ -428,7 +428,7 @@ impl MessageHandler> for DocumentMessageHand } GroupSelectedLayers => { // TODO: Add code that changes the insert index of the new folder based on the selected layer - let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers()).unwrap_or(LayerNodeIdentifier::ROOT); + let parent = self.metadata().deepest_common_ancestor(self.metadata().selected_layers(), true).unwrap_or(LayerNodeIdentifier::ROOT); let folder_id = generate_uuid(); @@ -816,17 +816,14 @@ impl MessageHandler> for DocumentMessageHand responses.add_front(DocumentMessage::DirtyRenderDocument); } StartTransaction => self.backup(responses), - ToggleLayerExpansion { layer_path } => { - self.layer_metadata_mut(&layer_path).expanded ^= true; - responses.add(DocumentStructureChanged); - responses.add(LayerChanged { affected_layer_path: layer_path }) - } - ToggleLayerVisibility { layer_path } => { - if let Ok(layer) = self.document_legacy.layer(&layer_path) { - let visible = layer.visible; - responses.add(DocumentOperation::SetLayerVisibility { path: layer_path, visible: !visible }); - responses.add(BroadcastEvent::DocumentIsDirty); + ToggleLayerExpansion { layer } => { + let layer = LayerNodeIdentifier::new(layer, self.network()); + if self.document_legacy.collapsed_folders.contains(&layer) { + self.document_legacy.collapsed_folders.retain(|&collapsed_layer| collapsed_layer != layer); + } else { + self.document_legacy.collapsed_folders.push(layer); } + responses.add(NodeGraphMessage::RunDocumentGraph); } Undo => { self.undo_in_progress = true; @@ -1074,13 +1071,6 @@ impl DocumentMessageHandler { self.layer_metadata.get(path).map(|layer| layer.selected).unwrap_or(false) } - pub fn selected_visible_layers(&self) -> impl Iterator { - self.selected_layers().filter(|path| match self.document_legacy.layer(path) { - Ok(layer) => layer.visible, - Err(_) => false, - }) - } - pub fn visible_layers(&self) -> impl Iterator { self.all_layers().filter(|path| match self.document_legacy.layer(path) { Ok(layer) => layer.visible, @@ -1100,7 +1090,7 @@ impl DocumentMessageHandler { for layer_node in folder.children(self.metadata()) { data.push(layer_node.to_node()); space += 1; - if layer_node.has_children(self.metadata()) { + if layer_node.has_children(self.metadata()) && !self.document_legacy.collapsed_folders.contains(&layer_node) { path.push(layer_node.to_node()); // TODO: Skip if folder is not expanded. @@ -1414,7 +1404,7 @@ impl DocumentMessageHandler { pub fn new_layer_parent(&self) -> LayerNodeIdentifier { self.metadata() - .deepest_common_ancestor(self.metadata().selected_layers()) + .deepest_common_ancestor(self.metadata().selected_layers(), false) .unwrap_or_else(|| self.metadata().active_artboard()) } diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index a41b01f4..ff96c3a4 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -625,7 +625,7 @@ impl MessageHandler { let mut modify_inputs = ModifyInputsContext::new(document, node_graph, responses); if let Some(layer) = modify_inputs.create_layer_with_insert_index(id, insert_index, parent) { modify_inputs.insert_vector_data(subpaths, layer); } - document.metadata.load_structure(&document.document_network); + document.load_network_structure(); } GraphOperationMessage::NewTextLayer { id, @@ -699,7 +699,7 @@ impl MessageHandler { if let Some(mut modify_inputs) = ModifyInputsContext::new_layer(&[id], document, node_graph, responses) { @@ -716,7 +716,7 @@ impl MessageHandler MessageHandler> for NodeGrap on: BroadcastEvent::SelectionChanged, send: Box::new(NodeGraphMessage::SelectedNodesUpdated.into()), }); - document.metadata.load_structure(&document.document_network); + document.load_network_structure(); responses.add(DocumentMessage::DocumentStructureChanged); } NodeGraphMessage::SelectedNodesUpdated => { @@ -803,7 +814,7 @@ impl<'a> MessageHandler> for NodeGrap let structure_changed = node_input.as_node().is_some() || input.as_node().is_some(); *node_input = input; if structure_changed { - document.metadata.load_structure(&document.document_network); + document.load_network_structure(); } } } @@ -882,7 +893,7 @@ impl<'a> MessageHandler> for NodeGrap } responses.add(NodeGraphMessage::SendGraph { should_rerender: false }); } - NodeGraphMessage::ToggleHidden => { + NodeGraphMessage::ToggleSelectedHidden => { if let Some(network) = document.document_network.nested_network(&self.network) { responses.add(DocumentMessage::StartTransaction); @@ -892,6 +903,12 @@ impl<'a> MessageHandler> for NodeGrap } } } + NodeGraphMessage::ToggleHidden { node_id } => { + if let Some(network) = document.document_network.nested_network(&self.network) { + let new_hidden = !network.disabled.contains(&node_id); + responses.add(NodeGraphMessage::SetHidden { node_id, hidden: new_hidden }); + } + } NodeGraphMessage::SetHidden { node_id, hidden } => { if let Some(network) = document.document_network.nested_network_mut(&self.network) { if !hidden { @@ -956,7 +973,7 @@ impl<'a> MessageHandler> for NodeGrap impl NodeGraphMessageHandler { pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList { if self.has_selection && graph_open { - actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden) + actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleSelectedHidden) } else { actions!(NodeGraphMessageDiscriminant;) } diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 4a21d720..057b506c 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -11,7 +11,6 @@ use crate::messages::prelude::*; use crate::messages::tool::utility_types::{HintData, HintGroup}; use crate::node_graph_executor::NodeGraphExecutor; -use document_legacy::document_metadata::LayerNodeIdentifier; use document_legacy::layers::style::RenderData; use graph_craft::document::NodeId; use graphene_core::text::Font; @@ -416,7 +415,7 @@ impl MessageHandler { if let Some(document) = self.active_document() { if let Ok(data) = serde_json::from_str::>(&data) { - let parent = document.metadata().deepest_common_ancestor(document.metadata().selected_layers()).unwrap_or(LayerNodeIdentifier::ROOT); + let parent = document.new_layer_parent(); responses.add(DocumentMessage::DeselectAllLayers); responses.add(DocumentMessage::StartTransaction); diff --git a/editor/src/messages/tool/common_functionality/path_outline.rs b/editor/src/messages/tool/common_functionality/path_outline.rs index 8640849f..ff712ac5 100644 --- a/editor/src/messages/tool/common_functionality/path_outline.rs +++ b/editor/src/messages/tool/common_functionality/path_outline.rs @@ -83,7 +83,7 @@ impl PathOutline { /// Performs an intersect test and generates a hovered overlay if necessary pub fn intersect_test_hovered(&mut self, input: &InputPreprocessorMessageHandler, document: &DocumentMessageHandler, responses: &mut VecDeque) { // Get the layer the user is hovering over - let intersection = document.metadata().click(input.mouse.position, &document.document_legacy.document_network); + let intersection = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network); let Some(hovered_layer) = intersection else { self.clear_hovered(responses); diff --git a/editor/src/messages/tool/common_functionality/pivot.rs b/editor/src/messages/tool/common_functionality/pivot.rs index acdd211b..4e3a36d8 100644 --- a/editor/src/messages/tool/common_functionality/pivot.rs +++ b/editor/src/messages/tool/common_functionality/pivot.rs @@ -52,7 +52,7 @@ impl Pivot { /// Recomputes the pivot position and transform. fn recalculate_pivot(&mut self, document: &DocumentMessageHandler) { - let mut layers = document.metadata().selected_visible_layers(); + let mut layers = document.document_legacy.selected_visible_layers(); let Some(first) = layers.next() else { // If no layers are selected then we revert things back to default self.normalized_pivot = DVec2::splat(0.5); @@ -73,7 +73,7 @@ impl Pivot { } else { // If more than one layer is selected we use the AABB with the mean of the pivots let xy_summation = document - .metadata() + .document_legacy .selected_visible_layers() .filter_map(|layer| graph_modification_utils::get_viewport_pivot(layer, &document.document_legacy)) .reduce(|a, b| a + b) @@ -81,7 +81,7 @@ impl Pivot { let pivot = xy_summation / selected_layers_count as f64; self.pivot = Some(pivot); - let [min, max] = document.metadata().selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]); + let [min, max] = document.document_legacy.selected_visible_layers_bounding_box_viewport().unwrap_or([DVec2::ZERO, DVec2::ONE]); self.normalized_pivot = (pivot - min) / (max - min); self.transform_from_normalized = DAffine2::from_translation(min) * DAffine2::from_scale(max - min); @@ -157,7 +157,7 @@ impl Pivot { /// Sets the viewport position of the pivot for all selected layers. pub fn set_viewport_position(&self, position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { - for layer in document.metadata().selected_visible_layers() { + for layer in document.document_legacy.selected_visible_layers() { let transform = Self::get_layer_pivot_transform(layer, document); let pivot = transform.inverse().transform_point2(position); // Only update the pivot when computed position is finite. Infinite can happen when scale is 0. diff --git a/editor/src/messages/tool/tool_messages/artboard_tool.rs b/editor/src/messages/tool/tool_messages/artboard_tool.rs index 0f4228ed..8d440eb5 100644 --- a/editor/src/messages/tool/tool_messages/artboard_tool.rs +++ b/editor/src/messages/tool/tool_messages/artboard_tool.rs @@ -150,11 +150,7 @@ impl ArtboardToolData { fn select_artboard(&mut self, document: &DocumentMessageHandler, render_data: &RenderData, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) -> bool { responses.add(DocumentMessage::StartTransaction); - let mut intersections = document - .document_legacy - .metadata - .click_xray(input.mouse.position) - .filter(|&layer| is_artboard(layer, &document.document_legacy)); + let mut intersections = document.document_legacy.click_xray(input.mouse.position).filter(|&layer| is_artboard(layer, &document.document_legacy)); responses.add(BroadcastEvent::DocumentIsDirty); if let Some(intersection) = intersections.next() { diff --git a/editor/src/messages/tool/tool_messages/fill_tool.rs b/editor/src/messages/tool/tool_messages/fill_tool.rs index 737eaa93..5642661c 100644 --- a/editor/src/messages/tool/tool_messages/fill_tool.rs +++ b/editor/src/messages/tool/tool_messages/fill_tool.rs @@ -68,7 +68,7 @@ impl Fsm for FillToolFsmState { let ToolMessage::Fill(event) = event else { return self; }; - let Some(layer_identifier) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) else { + let Some(layer_identifier) = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) else { return self; }; let layer = layer_identifier.to_path(); diff --git a/editor/src/messages/tool/tool_messages/gradient_tool.rs b/editor/src/messages/tool/tool_messages/gradient_tool.rs index 67fa4915..0e5ffe41 100644 --- a/editor/src/messages/tool/tool_messages/gradient_tool.rs +++ b/editor/src/messages/tool/tool_messages/gradient_tool.rs @@ -391,7 +391,7 @@ impl Fsm for GradientToolFsmState { SelectedGradient::update(&mut tool_data.selected_gradient, document, responses); } - for layer in document.metadata().selected_visible_layers() { + for layer in document.document_legacy.selected_visible_layers() { if let Some(gradient) = get_gradient(layer, &document.document_legacy) { let dragging = tool_data .selected_gradient @@ -526,7 +526,7 @@ impl Fsm for GradientToolFsmState { document.backup_nonmut(responses); GradientToolFsmState::Drawing } else { - let selected_layer = document.metadata().click(input.mouse.position, &document.document_legacy.document_network); + let selected_layer = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network); // Apply the gradient to the selected layer if let Some(layer) = selected_layer { diff --git a/editor/src/messages/tool/tool_messages/imaginate_tool.rs b/editor/src/messages/tool/tool_messages/imaginate_tool.rs index d4aed147..c9961cec 100644 --- a/editor/src/messages/tool/tool_messages/imaginate_tool.rs +++ b/editor/src/messages/tool/tool_messages/imaginate_tool.rs @@ -119,7 +119,7 @@ impl Fsm for ImaginateToolFsmState { match (self, event) { (_, ImaginateToolMessage::DocumentIsDirty | ImaginateToolMessage::SelectionChanged) => { tool_data.path_outlines.clear_selected(responses); - //tool_data.path_outlines.update_selected(document.selected_visible_layers(), document, responses, render_data); + //tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses, render_data); self } diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index d6eb0567..709fd1b6 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -246,7 +246,7 @@ impl PathToolData { PathToolFsmState::Dragging } // 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.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) { if shift { responses.add(NodeGraphMessage::SelectedNodesAdd { nodes: vec![layer.to_node()] }); } else { diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 6224ddad..4630a981 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -393,10 +393,10 @@ impl Fsm for SelectToolFsmState { tool_data.selected_layers_changed = selected_layers_count != tool_data.selected_layers_count; tool_data.selected_layers_count = selected_layers_count; - tool_data.path_outlines.update_selected(document.metadata().selected_layers(), document, responses); + tool_data.path_outlines.update_selected(document.document_legacy.selected_visible_layers(), document, responses); tool_data.path_outlines.intersect_test_hovered(input, document, responses); - match (document.metadata().selected_visible_layers_bounding_box_viewport(), tool_data.bounding_box_overlays.take()) { + match (document.document_legacy.selected_visible_layers_bounding_box_viewport(), tool_data.bounding_box_overlays.take()) { (None, Some(bounding_box_overlays)) => bounding_box_overlays.delete(responses), (Some(bounds), paths) => { let mut bounding_box_overlays = paths.unwrap_or_else(|| BoundingBoxOverlays::new(responses)); @@ -417,7 +417,7 @@ impl Fsm for SelectToolFsmState { } (_, SelectToolMessage::EditLayer) => { // Edit the clicked layer - if let Some(intersect) = document.metadata().click(input.mouse.position, &document.document_legacy.document_network) { + if let Some(intersect) = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network) { match tool_data.nested_selection_behavior { NestedSelectionBehavior::Shallowest => edit_layer_shallowest_manipulation(document, intersect, responses), NestedSelectionBehavior::Deepest => edit_layer_deepest_manipulation(intersect, &document.document_legacy, responses), @@ -450,8 +450,8 @@ impl Fsm for SelectToolFsmState { .map(|bounding_box| bounding_box.check_rotate(input.mouse.position)) .unwrap_or_default(); - let mut selected: Vec<_> = document.metadata().selected_visible_layers().collect(); - let intersection = document.metadata().click(input.mouse.position, &document.document_legacy.document_network); + let mut selected: Vec<_> = document.document_legacy.selected_visible_layers().collect(); + let intersection = document.document_legacy.click(input.mouse.position, &document.document_legacy.document_network); // If the user is dragging the bounding box bounds, go into ResizingBounds mode. // If the user is dragging the rotate trigger, go into RotatingBounds mode. @@ -706,7 +706,7 @@ impl Fsm for SelectToolFsmState { // Deselect layer if not snap dragging if !tool_data.has_dragged && input.keyboard.key(remove_from_selection) && tool_data.layer_selected_on_start.is_none() { let quad = tool_data.selection_quad(); - let intersection = document.metadata().intersect_quad(quad, &document.document_legacy.document_network); + let intersection = document.document_legacy.intersect_quad(quad, &document.document_legacy.document_network); if let Some(path) = intersection.last() { let replacement_selected_layers: Vec<_> = document.metadata().selected_layers().filter(|&layer| !path.starts_with(layer, document.metadata())).collect(); @@ -777,7 +777,7 @@ impl Fsm for SelectToolFsmState { (SelectToolFsmState::DrawingBox, SelectToolMessage::DragStop { .. } | SelectToolMessage::Enter) => { let quad = tool_data.selection_quad(); // 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.document_legacy.intersect_quad(quad, &document.document_legacy.document_network).collect(); responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: tool_data.layers_dragging.iter().map(|layer| layer.to_node()).collect(), }); diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index 631fc2c8..7dc2926e 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -274,7 +274,11 @@ impl TextToolData { fn interact(&mut self, state: TextToolFsmState, mouse: DVec2, document: &DocumentMessageHandler, render_data: &RenderData, responses: &mut VecDeque) -> TextToolFsmState { // Check if the user has selected an existing text layer - if let Some(clicked_text_layer_path) = document.metadata().click(mouse, document.network()).filter(|&layer| is_text_layer(layer, &document.document_legacy)) { + if let Some(clicked_text_layer_path) = document + .document_legacy + .click(mouse, document.network()) + .filter(|&layer| is_text_layer(layer, &document.document_legacy)) + { self.start_editing_layer(clicked_text_layer_path, state, document, render_data, responses); TextToolFsmState::Editing diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index cd79a6e6..c81c24d5 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -541,14 +541,14 @@ impl NodeGraphExecutor { } .to_string(), tooltip: format!("Layer id: {node_id}"), - visible: true, + visible: !document.document_network.disabled.contains(&layer.to_node()), layer_type: if document.metadata.is_folder(layer) { LayerDataTypeDiscriminant::Folder } else { LayerDataTypeDiscriminant::Layer }, layer_metadata: LayerMetadata { - expanded: layer.has_children(&document.metadata), + expanded: layer.has_children(&document.metadata) && !document.collapsed_folders.contains(&layer), selected: document.metadata.selected_layers_contains(layer), }, path: vec![node_id], diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index c0a92b74..25909638 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -700,14 +700,14 @@ impl JsEditorHandle { /// Toggle visibility of a layer from the layer list #[wasm_bindgen(js_name = toggleLayerVisibility)] pub fn toggle_layer_visibility(&self, layer_path: Vec) { - let message = DocumentMessage::ToggleLayerVisibility { layer_path }; + let message = NodeGraphMessage::ToggleHidden { node_id: *layer_path.last().unwrap() }; self.dispatch(message); } /// Toggle expansions state of a layer from the layer list #[wasm_bindgen(js_name = toggleLayerExpansion)] pub fn toggle_layer_expansion(&self, layer_path: Vec) { - let message = DocumentMessage::ToggleLayerExpansion { layer_path }; + let message = DocumentMessage::ToggleLayerExpansion { layer: *layer_path.last().unwrap() }; self.dispatch(message); } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index e4fb499c..3560d47d 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -809,9 +809,14 @@ impl NodeNetwork { return; }; - if self.disabled.contains(&id) { + if node.implementation != DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()) && self.disabled.contains(&id) { node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()); - node.inputs.drain(1..); + if node.name == "Layer" { + // Connect layer node to the graphic group below + node.inputs.drain(..7); + } else { + node.inputs.drain(1..); + } self.nodes.insert(id, node); return; }