diff --git a/editor/src/application.rs b/editor/src/application.rs index e3c7c24d09..b3695280fc 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -1,5 +1,8 @@ use crate::dispatcher::Dispatcher; +use crate::messages::portfolio::document::node_graph::generate_node_graph_overlay::generate_node_graph_overlay; use crate::messages::prelude::*; +use graph_craft::document::{NodeInput, NodeNetwork}; +use graphene_std::node_graph_overlay::types::NodeGraphOverlayData; pub use graphene_std::uuid::*; // TODO: serialize with serde to save the current editor state @@ -30,6 +33,31 @@ impl Editor { pub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque) -> Result<(), String> { self.dispatcher.poll_node_graph_evaluation(responses) } + + pub fn generate_node_graph_overlay_network(&mut self) -> Option { + let Some(active_document) = self.dispatcher.message_handlers.portfolio_message_handler.active_document_mut() else { + return None; + }; + let breadcrumb_network_path = &active_document.breadcrumb_network_path; + let nodes_to_render = active_document.network_interface.collect_nodes( + &active_document.node_graph_handler.node_graph_errors, + self.dispatcher.message_handlers.preferences_message_handler.graph_wire_style, + breadcrumb_network_path, + ); + let previewed_node = active_document.network_interface.previewed_node(breadcrumb_network_path); + let node_graph_render_data = NodeGraphOverlayData { + nodes_to_render, + open: active_document.graph_view_overlay_open, + in_selected_network: &active_document.selection_network_path == breadcrumb_network_path, + previewed_node, + }; + let node_graph_overlay_node = generate_node_graph_overlay(node_graph_render_data, active_document.graph_fade_artwork_percentage); + Some(NodeNetwork { + exports: vec![NodeInput::node(NodeId(0), 0)], + nodes: vec![(NodeId(0), node_graph_overlay_node)].into_iter().collect(), + ..Default::default() + }) + } } impl Default for Editor { diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 3942b3d3ab..dea15382f8 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -20,11 +20,11 @@ pub struct DispatcherMessageHandlers { defer_message_handler: DeferMessageHandler, dialog_message_handler: DialogMessageHandler, globals_message_handler: GlobalsMessageHandler, - input_preprocessor_message_handler: InputPreprocessorMessageHandler, + pub input_preprocessor_message_handler: InputPreprocessorMessageHandler, key_mapping_message_handler: KeyMappingMessageHandler, layout_message_handler: LayoutMessageHandler, pub portfolio_message_handler: PortfolioMessageHandler, - preferences_message_handler: PreferencesMessageHandler, + pub preferences_message_handler: PreferencesMessageHandler, tool_message_handler: ToolMessageHandler, } diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 5e4d64a6dc..5bafd46ede 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -119,10 +119,10 @@ pub struct DocumentMessageHandler { pub(crate) path: Option, /// Path to network currently viewed in the node graph overlay. This will eventually be stored in each panel, so that multiple panels can refer to different networks #[serde(skip)] - breadcrumb_network_path: Vec, + pub breadcrumb_network_path: Vec, /// Path to network that is currently selected. Updated based on the most recently clicked panel. #[serde(skip)] - selection_network_path: Vec, + pub selection_network_path: Vec, /// Stack of document network snapshots for previous history states. #[serde(skip)] document_undo_history: VecDeque, diff --git a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs index f66fdd4783..8ed797f360 100644 --- a/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs +++ b/editor/src/messages/portfolio/menu_bar/menu_bar_message_handler.rs @@ -18,7 +18,7 @@ pub struct MenuBarMessageHandler { pub has_selection_history: (bool, bool), pub message_logging_verbosity: MessageLoggingVerbosity, pub reset_node_definitions_on_open: bool, - pub native_node_graph_render: bool, + pub should_render_svelte_nodes: bool, pub make_path_editable_is_allowed: bool, pub data_panel_open: bool, pub layers_panel_open: bool, @@ -49,7 +49,7 @@ impl LayoutHolder for MenuBarMessageHandler { let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names; let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents; let reset_node_definitions_on_open = self.reset_node_definitions_on_open; - let native_node_graph_render = self.native_node_graph_render; + let should_render_svelte_nodes = self.should_render_svelte_nodes; let make_path_editable_is_allowed = self.make_path_editable_is_allowed; let menu_bar_entries = vec![ @@ -699,8 +699,8 @@ impl LayoutHolder for MenuBarMessageHandler { ..MenuBarEntry::default() }], vec![MenuBarEntry { - label: "Native Node Graph UI Render".into(), - icon: Some(if native_node_graph_render { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()), + label: "HTML Node Graph Render".into(), + icon: Some(if should_render_svelte_nodes { "CheckboxChecked" } else { "CheckboxUnchecked" }.into()), action: MenuBarEntry::create_action(|_| NodeGraphMessage::ToggleNativeNodeGraphRender.into()), ..MenuBarEntry::default() }], diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index b544ff396c..9bfe167f7c 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -223,6 +223,15 @@ } +{#if $nodeGraph.shouldRenderSvelteNodes} +
+ + + {/each} + {/each} + {#each Array.from($nodeGraph.nodesToRender) as [nodeId, nodeToRender]} + {#if nodeToRender.nodeOrLayer.node !== undefined && $nodeGraph.visibleNodes.has(nodeId)} + {@const nodeMetadata = nodeToRender.metadata} + {@const node = nodeToRender.nodeOrLayer.node} + {@const exposedInputsOutputs = collectExposedInputsOutputs(node.inputs, node.outputs)} + {@const clipPathId = String(Math.random()).substring(2)} + {@const description = (nodeMetadata.reference && $nodeGraph.nodeDescriptions.get(nodeMetadata.reference)) || undefined} +
+ {#if nodeMetadata.errors} + {node.errors} + {node.errors} + {/if} + +
+ + + {nodeMetadata.displayName} +
+ + {#if exposedInputsOutputs.length > 0} +
+ {#each exposedInputsOutputs as [input, output]} +
+ + {input?.name ?? output?.name ?? ""} + +
+ {/each} +
+ {/if} + +
+ {#each node.inputs as input} + {#if input !== undefined} + + {inputTooltip(input)} + + + {/if} + {/each} +
+ +
+ {#each node.outputs as output} + {#if output !== undefined} + + {outputTooltip(output)} + + + {/if} + {/each} +
+ + + + + + + +
+ {/if} + {/each} +
+{:else} +
{@html $nodeGraph.nativeNodeGraphSVGString}
+{/if} {#each Array.from($nodeGraph.nodesToRender) as [_, nodeToRender]} {#each nodeToRender.wires as [wire, thick, dataType]} @@ -767,6 +878,199 @@ background-repeat: repeat; image-rendering: pixelated; mix-blend-mode: screen; + opacity: var(--fade-artwork); + } + } + + .native-node-graph-ui { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + .breadcrumb-trail-buttons { + margin-top: 8px; + margin-left: 8px; + } + + .context-menu { + width: max-content; + position: absolute; + box-sizing: border-box; + padding: 5px; + z-index: 3; + background-color: var(--color-3-darkgray); + border-radius: 4px; + + .toggle-layer-or-node .text-label { + line-height: 24px; + margin-right: 8px; + } + + .merge-selected-nodes { + justify-content: center; + } + } + + .click-targets { + position: absolute; + pointer-events: none; + width: 100%; + height: 100%; + z-index: 10; + + svg { + overflow: visible; + width: 100%; + height: 100%; + stroke-width: 1; + fill: none; + + .layer { + stroke: yellow; + } + + .node { + stroke: blue; + } + + .connector { + stroke: green; + } + + .visibility { + stroke: red; + } + + .all-nodes-bounding-box { + stroke: purple; + } + + .modify-import-export { + stroke: orange; + } + } + } + + .wires { + pointer-events: none; + position: absolute; + width: 100%; + height: 100%; + + svg { + width: 100%; + height: 100%; + overflow: visible; + + path { + fill: none; + stroke: var(--data-color-dim); + stroke-width: var(--data-line-width); + stroke-dasharray: var(--data-dasharray); + } + } + } + + .imports-and-exports { + width: 100%; + height: 100%; + position: absolute; + // Keeps the connectors above the wires + z-index: 1; + + .connector { + position: absolute; + width: 8px; + height: 8px; + margin-top: 4px; + margin-left: 5px; + top: calc(var(--offset-top) * 24px); + left: calc(var(--offset-left) * 24px); + } + + .reorder-bar { + position: absolute; + top: calc(var(--offset-top) * 24px); + left: calc(var(--offset-left) * 24px); + width: 50px; + height: 2px; + background: white; + } + + .plus { + position: absolute; + top: calc(var(--offset-top) * 24px); + left: calc(var(--offset-left) * 24px); + } + + .edit-import-export { + position: absolute; + display: flex; + align-items: center; + top: calc(var(--offset-top) * 24px); + margin-top: -5px; + height: 24px; + + &.separator-bottom::after, + &.separator-top::before { + content: ""; + position: absolute; + background: var(--color-8-uppergray); + height: 1px; + left: -4px; + right: -4px; + } + + &.separator-bottom::after { + bottom: -1px; + } + + &.separator-top::before { + top: 0; + } + + &.import { + right: calc(100% - var(--offset-left) * 24px); + } + + &.export { + left: calc(var(--offset-left) * 24px + 17px); + } + + .import-text { + text-align: right; + text-wrap: nowrap; + } + + .export-text { + text-wrap: nowrap; + } + + .import-text-input { + text-align: right; + } + + .remove-button-import { + margin-left: 3px; + } + + .remove-button-export { + margin-right: 3px; + } + + .reorder-drag-grip { + width: 8px; + height: 24px; + background-position: 2px 8px; + border-radius: 2px; + margin: -6px 0; + background-image: var(--icon-drag-grip-hover); + } + } + } } }