diff --git a/editor/src/messages/layout/utility_types/layout_widget.rs b/editor/src/messages/layout/utility_types/layout_widget.rs index ac136311d2..a91cf4c175 100644 --- a/editor/src/messages/layout/utility_types/layout_widget.rs +++ b/editor/src/messages/layout/utility_types/layout_widget.rs @@ -358,6 +358,7 @@ pub struct WidgetSection { pub description: String, pub visible: bool, pub pinned: bool, + pub expanded: bool, pub id: u64, pub layout: Layout, } @@ -386,12 +387,13 @@ impl LayoutGroup { Self::Table(WidgetTable { rows, unstyled }) } - pub fn section(name: impl Into, description: impl Into, visible: bool, pinned: bool, id: u64, layout: Layout) -> Self { + pub fn section(name: impl Into, description: impl Into, visible: bool, pinned: bool, expanded: bool, id: u64, layout: Layout) -> Self { Self::Section(WidgetSection { name: name.into(), description: description.into(), visible, pinned, + expanded, id, layout, }) @@ -485,6 +487,7 @@ impl Diffable for LayoutGroup { description: current_description, visible: current_visible, pinned: current_pinned, + expanded: current_expanded, id: current_id, layout: current_layout, }), @@ -493,6 +496,7 @@ impl Diffable for LayoutGroup { description: new_description, visible: new_visible, pinned: new_pinned, + expanded: new_expanded, id: new_id, layout: new_layout, }), @@ -504,6 +508,7 @@ impl Diffable for LayoutGroup { || *current_description != new_description || *current_visible != new_visible || *current_pinned != new_pinned + || *current_expanded != new_expanded || *current_id != new_id { // Update self to reflect new changes @@ -511,6 +516,7 @@ impl Diffable for LayoutGroup { current_description.clone_from(&new_description); *current_visible = new_visible; *current_pinned = new_pinned; + *current_expanded = new_expanded; *current_id = new_id; current_layout.clone_from(&new_layout); @@ -520,6 +526,7 @@ impl Diffable for LayoutGroup { description: new_description, visible: new_visible, pinned: new_pinned, + expanded: new_expanded, id: new_id, layout: new_layout, }) diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 0e6e02043c..a9cab53dc1 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -85,6 +85,7 @@ pub struct DocumentMessageHandler { /// 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, + /// The full Git commit hash of the Graphite repository that was used to build the editor. /// We save this to provide a hint about which version of the editor was used to create the document. pub commit_hash: String, @@ -158,6 +159,7 @@ impl Default for DocumentMessageHandler { // ============================================ network_interface: default_document_network_interface(), collapsed: CollapsedLayers::default(), + commit_hash: GRAPHITE_GIT_COMMIT_HASH.to_string(), document_ptz: PTZ::default(), render_mode: RenderMode::default(), @@ -228,6 +230,7 @@ impl MessageHandler> for DocumentMes document_name: self.name.as_str(), executor, persistent_data, + properties_panel_open, }; self.properties_panel_message_handler.process_message(message, responses, context); diff --git a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs index 16c70b119b..d094bb6dbb 100644 --- a/editor/src/messages/portfolio/document/graph_operation/utility_types.rs +++ b/editor/src/messages/portfolio/document/graph_operation/utility_types.rs @@ -125,6 +125,7 @@ impl<'a> ModifyInputsContext<'a> { pub fn create_layer(&mut self, new_id: NodeId) -> LayerNodeIdentifier { let new_merge_node = resolve_network_node_type("Merge").expect("Merge node").default_node_template(); self.network_interface.insert_node(new_id, new_merge_node, &[]); + self.responses.add(PropertiesPanelMessage::SetSectionExpanded { node_id: new_id.0, expanded: false }); LayerNodeIdentifier::new(new_id, self.network_interface) } diff --git a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs index 06c64faa64..bb0f4336a7 100644 --- a/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs +++ b/editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs @@ -28,6 +28,8 @@ use graphene_std::*; use serde_json::Value; use std::collections::{HashMap, VecDeque}; +pub const MERGE_NODE_IDENTIFIER: &str = "Merge"; + pub struct NodePropertiesContext<'a> { pub persistent_data: &'a PersistentData, pub responses: &'a mut VecDeque, @@ -214,7 +216,7 @@ fn document_node_definitions() -> HashMap NodeTemplate { self.node_template_input_override(self.node_template.document_node.inputs.clone().into_iter().map(Some)) } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs index 4ec91d9981..8dcb373356 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs @@ -213,6 +213,10 @@ pub enum NodeGraphMessage { node_id: NodeId, pinned: bool, }, + SetCollapsed { + node_id: NodeId, + collapsed: bool, + }, SetVisibility { node_id: NodeId, visible: bool, 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 b7f6992fc5..e8e3a9190f 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 @@ -1935,6 +1935,9 @@ impl<'a> MessageHandler> for NodeG NodeGraphMessage::SetPinned { node_id, pinned } => { network_interface.set_pinned(&node_id, selection_network_path, pinned); } + NodeGraphMessage::SetCollapsed { node_id, collapsed } => { + network_interface.set_collapsed(&node_id, selection_network_path, collapsed); + } NodeGraphMessage::SetVisibility { node_id, visible } => { network_interface.set_visibility(&node_id, selection_network_path, visible); } @@ -1944,6 +1947,8 @@ impl<'a> MessageHandler> for NodeG } responses.add(NodeGraphMessage::UpdateActionButtons); responses.add(NodeGraphMessage::SendGraph); + responses.add(NodeGraphMessage::UpdateLayerPanel); + responses.add(PortfolioMessage::AutoSaveActiveDocument); responses.add(PropertiesPanelMessage::Refresh); } diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index efa508b3ff..09903fcceb 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1796,7 +1796,17 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper if layout.is_empty() { layout = node_no_properties(node_id, context); } - let name = context.network_interface.implementation_name(&node_id, context.selection_network_path); + let mut name = context.network_interface.implementation_name(&node_id, context.selection_network_path); + if name == "Custom Node" { + if let Some(display_name) = context + .network_interface + .node_metadata(&node_id, context.selection_network_path) + .map(|metadata| metadata.persistent_metadata.display_name.clone()) + .filter(|name| !name.is_empty()) + { + name = display_name; + } + } let description = context .network_interface @@ -1809,8 +1819,10 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper let visible = context.network_interface.is_visible(&node_id, context.selection_network_path); let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path); + let collapsed = context.network_interface.is_collapsed(&node_id, context.selection_network_path); + let expanded = !collapsed; - LayoutGroup::section(name, description, visible, pinned, node_id.0, Layout(layout)) + LayoutGroup::section(name, description, visible, pinned, expanded, node_id.0, Layout(layout)) } /// Fill Node Widgets LayoutGroup diff --git a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs index a87d8fb07d..954c909dee 100644 --- a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs +++ b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message.rs @@ -6,4 +6,6 @@ pub enum PropertiesPanelMessage { // Messages Clear, Refresh, + SetAllSectionsExpanded { expanded: bool }, + SetSectionExpanded { node_id: u64, expanded: bool }, } diff --git a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs index b5706ec1e5..09e472fec7 100644 --- a/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs +++ b/editor/src/messages/portfolio/document/properties_panel/properties_panel_message_handler.rs @@ -60,6 +60,34 @@ impl MessageHandler> f layout_target: LayoutTarget::PropertiesPanel, }); } + PropertiesPanelMessage::SetAllSectionsExpanded { expanded } => { + let mut layout = { + let mut node_properties_context = NodePropertiesContext { + persistent_data, + responses, + network_interface, + selection_network_path, + document_name, + executor, + }; + Layout(NodeGraphMessageHandler::collate_properties(&mut node_properties_context)) + }; + + let node_ids = Self::update_all_section_expansion_recursive(&mut layout.0, expanded, responses); + if !node_ids.is_empty() { + responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids }); + } + + responses.add(LayoutMessage::SendLayout { + layout, + layout_target: LayoutTarget::PropertiesPanel, + }); + } + PropertiesPanelMessage::SetSectionExpanded { node_id, expanded } => { + let node_id = NodeId(node_id); + responses.add(NodeGraphMessage::SetCollapsed { node_id, collapsed: !expanded }); + responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] }); + } } } @@ -67,3 +95,22 @@ impl MessageHandler> f actions!(PropertiesMessageDiscriminant;) } } + +impl PropertiesPanelMessageHandler { + fn update_all_section_expansion_recursive(layout: &mut [LayoutGroup], expanded: bool, responses: &mut VecDeque) -> Vec { + let mut node_ids = Vec::new(); + for group in layout { + if let LayoutGroup::Section(WidgetSection { + id, layout, expanded: group_expanded, .. + }) = group + { + *group_expanded = expanded; + let node_id = NodeId(*id); + node_ids.push(node_id); + responses.add(NodeGraphMessage::SetCollapsed { node_id, collapsed: !expanded }); + node_ids.extend(Self::update_all_section_expansion_recursive(&mut layout.0, expanded, responses)); + } + } + node_ids + } +} 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 ac95dfce84..050e18a35a 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -1044,6 +1044,17 @@ impl NodeNetworkInterface { node_metadata.persistent_metadata.pinned } + pub fn is_collapsed(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { + let Some(node_metadata) = self.node_metadata(node_id, network_path) else { + log::error!("Could not get persistent node metadata in is_collapsed for node {node_id}"); + return false; + }; + node_metadata + .persistent_metadata + .collapsed + .unwrap_or_else(|| self.implementation_name(node_id, network_path) == "Merge") + } + pub fn is_visible(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool { let Some(node) = self.document_node(node_id, network_path) else { log::error!("Could not get node in is_visible"); @@ -4508,6 +4519,16 @@ impl NodeNetworkInterface { self.transaction_modified(); } + pub fn set_collapsed(&mut self, node_id: &NodeId, network_path: &[NodeId], collapsed: bool) { + let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { + log::error!("Could not get node {node_id} in set_collapsed"); + return; + }; + + node_metadata.persistent_metadata.collapsed = Some(collapsed); + self.transaction_modified(); + } + pub fn set_visibility(&mut self, node_id: &NodeId, network_path: &[NodeId], is_visible: bool) { let Some(network) = self.network_mut(network_path) else { return; @@ -6268,6 +6289,8 @@ pub struct DocumentNodePersistentMetadata { /// Indicates that the node will be shown in the Properties panel when it would otherwise be empty, letting a user easily edit its properties by just deselecting everything. #[serde(default)] pub pinned: bool, + #[serde(default)] + pub collapsed: Option, /// Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. /// All fields in NodeTypePersistentMetadata should automatically be updated by using the network interface API pub node_type_metadata: NodeTypePersistentMetadata, diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/deserialization.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/deserialization.rs index 5e824e2ad7..75d0ed9639 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/deserialization.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/deserialization.rs @@ -147,6 +147,7 @@ impl From for DocumentNodePersist output_names: old.output_names, locked: old.locked, pinned: old.pinned, + collapsed: None, node_type_metadata: old.node_type_metadata, network_metadata: old.network_metadata, } diff --git a/frontend/src/components/widgets/WidgetSection.svelte b/frontend/src/components/widgets/WidgetSection.svelte index 0314eb3114..cbb1bdd4c6 100644 --- a/frontend/src/components/widgets/WidgetSection.svelte +++ b/frontend/src/components/widgets/WidgetSection.svelte @@ -3,6 +3,7 @@ import type { LayoutTarget, WidgetSection as WidgetSectionData } from "@graphite/../wasm/pkg/graphite_wasm"; import type { Editor } from "@graphite/editor"; + import { operatingSystem } from "@graphite/utility-functions/platform"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import IconButton from "@graphite/components/widgets/buttons/IconButton.svelte"; @@ -16,14 +17,24 @@ export { className as class }; export let classes: Record = {}; - let expanded = true; + $: expanded = widgetData.expanded; const editor = getContext("editor"); - -