From 4c92c48cc36f17b4f16ddb6acf5bd9bb41f989f2 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Sep 2025 16:53:07 -0700 Subject: [PATCH] Fix misc errors and cleanup after rebases --- editor/src/application.rs | 1 + editor/src/consts.rs | 2 +- editor/src/dispatcher.rs | 1 + .../messages/defer/defer_message_handler.rs | 16 +- .../src/messages/frontend/frontend_message.rs | 5 - .../input_preprocessor_message_handler.rs | 7 +- .../document/document_message_handler.rs | 2 + .../document/node_graph/node_graph_message.rs | 7 +- .../node_graph/node_graph_message_handler.rs | 81 ++++---- .../document/node_graph/utility_types.rs | 181 +----------------- .../utility_types/network_interface.rs | 29 ++- .../network_interface/node_graph.rs | 43 +++-- .../portfolio/document/utility_types/wires.rs | 2 +- .../messages/tool/tool_messages/path_tool.rs | 2 +- editor/src/node_graph_executor.rs | 10 +- frontend/src/components/views/Graph.svelte | 2 +- frontend/src/io-managers/input.ts | 3 +- frontend/src/messages.ts | 69 +++---- frontend/src/state-providers/node-graph.ts | 19 +- frontend/wasm/src/editor_api.rs | 131 +++++-------- frontend/wasm/src/lib.rs | 4 +- frontend/wasm/src/native_communcation.rs | 2 +- .../src/node_graph_overlay/nodes_and_wires.rs | 17 +- .../gcore/src/node_graph_overlay/types.rs | 1 + node-graph/gsvg-renderer/src/renderer.rs | 5 + 25 files changed, 206 insertions(+), 436 deletions(-) diff --git a/editor/src/application.rs b/editor/src/application.rs index 5b3e64d275..b1db103e75 100644 --- a/editor/src/application.rs +++ b/editor/src/application.rs @@ -50,6 +50,7 @@ impl Editor { open: active_document.graph_view_overlay_open, in_selected_network: &active_document.selection_network_path == breadcrumb_network_path, previewed_node, + thumbnails: active_document.node_graph_handler.thumbnails.clone() }; let opacity = active_document.graph_fade_artwork_percentage; let node_graph_overlay_node = generate_node_graph_overlay(node_graph_render_data, opacity); diff --git a/editor/src/consts.rs b/editor/src/consts.rs index f2044abaea..13144943d9 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -4,7 +4,7 @@ pub const EXPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72; pub const EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP: u32 = 120; pub const IMPORTS_TO_TOP_EDGE_PIXEL_GAP: u32 = 72; pub const IMPORTS_TO_LEFT_EDGE_PIXEL_GAP: u32 = 120; -pub const TOOLTIP_DELAY: u32 = 800; +pub const INPUT_TOOLTIP_DELAY: u64 = 800; // VIEWPORT pub const VIEWPORT_ZOOM_WHEEL_RATE: f64 = (1. / 600.) * 3.; diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 2389012a2c..df2f6bd9a3 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -59,6 +59,7 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[ const DEBUG_MESSAGE_BLOCK_LIST: &[MessageDiscriminant] = &[ MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(EventMessageDiscriminant::AnimationFrame)), MessageDiscriminant::Animation(AnimationMessageDiscriminant::IncrementFrameCounter), + MessageDiscriminant::Defer(DeferMessageDiscriminant::CheckDeferredMessages), ]; // TODO: Find a way to combine these with the list above. We use strings for now since these are the standard variant names used by multiple messages. But having these also type-checked would be best. const DEBUG_MESSAGE_ENDING_BLOCK_LIST: &[&str] = &["PointerMove", "PointerOutsideViewport", "Overlays", "Draw", "CurrentTime", "Time"]; diff --git a/editor/src/messages/defer/defer_message_handler.rs b/editor/src/messages/defer/defer_message_handler.rs index 9b6a1f8f9c..5b25b4832b 100644 --- a/editor/src/messages/defer/defer_message_handler.rs +++ b/editor/src/messages/defer/defer_message_handler.rs @@ -1,15 +1,11 @@ -use std::{ - collections::BTreeMap, - ops::Bound, - time::{Duration, Instant}, -}; +use std::collections::BTreeMap; use crate::messages::prelude::*; #[derive(ExtractField)] pub struct DeferMessageContext<'a> { pub portfolio: &'a PortfolioMessageHandler, - pub time: Instant, + pub time: u64, } #[derive(Debug, Default, ExtractField)] @@ -17,7 +13,7 @@ pub struct DeferMessageHandler { after_graph_run: HashMap>, after_viewport_resize: Vec, current_graph_submission_id: u64, - after_time_elapsed: BTreeMap, + after_time_elapsed: BTreeMap, } #[message_handler_data] @@ -58,11 +54,11 @@ impl MessageHandler> for DeferMessageHandl } } DeferMessage::RequestDeferredMessage { timeout, message } => { - self.after_time_elapsed.insert(context.time + timeout, *message); + self.after_time_elapsed.insert(context.time + timeout.as_millis() as u64, *message); } DeferMessage::CheckDeferredMessages => { - let after_current_time = self.after_time_elapsed.split_off((Bound::Unbounded, Bound::Excluded(context.time))); - for (_, message) in std::mem::replace(self.after_time_elapsed, after_current_time) { + let after_current_time = self.after_time_elapsed.split_off(&context.time); + for (_, message) in std::mem::replace(&mut self.after_time_elapsed, after_current_time) { responses.add(message); } } diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 7b8a24dfed..3162a5c79c 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -175,10 +175,6 @@ pub enum FrontendMessage { #[serde(rename = "exportIndex")] index: Option, }, - UpdateLayerWidths { - #[serde(rename = "layerWidths")] - layer_widths: HashMap, - }, UpdateDialogButtons { #[serde(rename = "layoutTarget")] layout_target: LayoutTarget, @@ -269,7 +265,6 @@ pub enum FrontendMessage { UpdateMouseCursor { cursor: MouseCursorIcon, }, - RequestNativeNodeGraphRender, UpdateNativeNodeGraphSVG { #[serde(rename = "svgString")] svg_string: String, diff --git a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs index a5ccec838a..060449d317 100644 --- a/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs +++ b/editor/src/messages/input_preprocessor/input_preprocessor_message_handler.rs @@ -4,7 +4,7 @@ use crate::messages::input_mapper::utility_types::misc::FrameTimeInfo; use crate::messages::portfolio::utility_types::KeyboardPlatformLayout; use crate::messages::prelude::*; use glam::DVec2; -use std::time::{Duration, Instant}; +use std::time::Duration; #[derive(ExtractField)] pub struct InputPreprocessorMessageContext { @@ -14,7 +14,7 @@ pub struct InputPreprocessorMessageContext { #[derive(Debug, Default, ExtractField)] pub struct InputPreprocessorMessageHandler { pub frame_time: FrameTimeInfo, - pub time: Instant, + pub time: u64, pub keyboard: KeyStates, pub mouse: MouseState, pub viewport_bounds: ViewportBounds, @@ -43,6 +43,7 @@ impl MessageHandler f .into(), ], }); + responses.add(NodeGraphMessage::UpdateNodeGraphTopRight); } InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => { self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses); @@ -114,7 +115,7 @@ impl MessageHandler f } InputPreprocessorMessage::CurrentTime { timestamp } => { responses.add(AnimationMessage::SetTime { time: timestamp as f64 }); - self.time = Instant::from(timestamp); + self.time = timestamp; self.frame_time.advance_timestamp(Duration::from_millis(timestamp)); } InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => { diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 71373d0ebb..cb4b3502e2 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -476,6 +476,7 @@ impl MessageHandler> for DocumentMes self.selection_network_path.clone_from(&self.breadcrumb_network_path); responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::ZoomCanvasToFitAll); + responses.add(NodeGraphMessage::UpdateNodeGraphTopRight); } DocumentMessage::Escape => { if self.node_graph_handler.drag_start.is_some() { @@ -504,6 +505,7 @@ impl MessageHandler> for DocumentMes } responses.add(NodeGraphMessage::SendGraph); responses.add(DocumentMessage::PTZUpdate); + responses.add(NodeGraphMessage::UpdateNodeGraphTopRight); } DocumentMessage::FlipSelectedLayers { flip_axis } => { let scale = match flip_axis { 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 9fa473a569..64748fd5c8 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 @@ -3,7 +3,7 @@ use crate::messages::input_mapper::utility_types::input_keyboard::Key; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::portfolio::document::utility_types::network_interface::{ImportOrExport, InputConnector, NodeTemplate, OutputConnector}; use crate::messages::prelude::*; -use glam::IVec2; +use glam::{DVec2, IVec2}; use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput}; use graph_craft::proto::GraphErrors; @@ -112,6 +112,7 @@ pub enum NodeGraphMessage { PointerOutsideViewport { shift: Key, }, + UpdateNodeGraphTopRight, ShakeNode, RemoveImport { import_index: usize, @@ -215,7 +216,9 @@ pub enum NodeGraphMessage { SetLockedOrVisibilitySideEffects { node_ids: Vec, }, - TryDisplayTooltip, + TryDisplayTooltip { + initial_position: DVec2, + }, UpdateBoxSelection, UpdateImportsExports, UpdateLayerPanel, 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 f07c503dd3..6fe6511f82 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 @@ -1,6 +1,6 @@ use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart}; use super::{document_node_definitions, node_properties}; -use crate::consts::{GRID_SIZE, TOOLTIP_DELAY}; +use crate::consts::*; use crate::messages::input_mapper::utility_types::macros::action_keys; use crate::messages::layout::utility_types::widget_prelude::*; use crate::messages::portfolio::document::document_message_handler::navigation_controls; @@ -30,6 +30,7 @@ use graphene_std::*; use kurbo::{DEFAULT_ACCURACY, Shape}; use renderer::Quad; use std::cmp::Ordering; +use std::time::Duration; #[derive(Debug, ExtractField)] pub struct NodeGraphMessageContext<'a> { @@ -1149,19 +1150,21 @@ impl<'a> MessageHandler> for NodeG .unwrap_or_else(|| modify_import_export.reorder_imports_exports.input_ports().count() + 1), ); responses.add(FrontendMessage::UpdateExportReorderIndex { index: self.end_index }); - } else if !self.hovering_input && !self.hovering_output { + } else if !self.hovering_input && !self.hovering_output && !self.hovering_node { if network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() { - self.hovering_input = true; responses.add(DeferMessage::RequestDeferredMessage { - message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()), - timeout: Duration::from_millis(TOOLTIP_DELAY), + message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()), + timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY), }); - } - if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() { - self.hovering_output = true; + } else if network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() { responses.add(DeferMessage::RequestDeferredMessage { - message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()), - timeout: Duration::from_millis(TOOLTIP_DELAY), + message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()), + timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY), + }) + } else if network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() { + responses.add(DeferMessage::RequestDeferredMessage { + message: Box::new(NodeGraphMessage::TryDisplayTooltip { initial_position: ipp.mouse.position }.into()), + timeout: Duration::from_millis(INPUT_TOOLTIP_DELAY), }) } } else if self.hovering_input { @@ -1174,16 +1177,8 @@ impl<'a> MessageHandler> for NodeG self.hovering_output = false; responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() }); } - } else if !self.hovering_node { - if network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) { - self.hovering_node = true; - responses.add(DeferMessage::RequestDeferredMessage { - message: Box::new(NodeGraphMessage::TryDisplayTooltip.into()), - timeout: Duration::from_millis(TOOLTIP_DELAY), - }) - } } else if self.hovering_node { - if !network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) { + if !network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path).is_some() { self.hovering_node = false; responses.add(FrontendMessage::UpdateTooltip { position: None, text: String::new() }); } @@ -1449,6 +1444,10 @@ impl<'a> MessageHandler> for NodeG self.auto_panning.stop(&messages, responses); } } + NodeGraphMessage::UpdateNodeGraphTopRight => { + network_interface.set_node_graph_width(ipp.viewport_bounds.size().x, breadcrumb_network_path); + responses.add(NodeGraphMessage::UpdateImportsExports); + } NodeGraphMessage::ShakeNode => { let Some(drag_start) = &self.drag_start else { log::error!("Drag start should be initialized when shaking a node"); @@ -1849,8 +1848,10 @@ impl<'a> MessageHandler> for NodeG responses.add(NodeGraphMessage::SetVisibility { node_id, visible }); responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] }); } - NodeGraphMessage::TryDisplayTooltip => { - if let Some(input) = network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path) { + NodeGraphMessage::TryDisplayTooltip { initial_position } => { + if initial_position != ipp.mouse.position { + return; + } else if let Some(input) = network_interface.input_connector_from_click(ipp.mouse.position, breadcrumb_network_path) { let text = network_interface.input_tooltip_text(&input, breadcrumb_network_path); if let Some(position) = network_interface.input_position(&input, breadcrumb_network_path) { let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { @@ -1862,6 +1863,7 @@ impl<'a> MessageHandler> for NodeG y: position.y as i32, }; responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text }); + self.hovering_input = true; } } else if let Some(output) = network_interface.output_connector_from_click(ipp.mouse.position, breadcrumb_network_path) { let text = network_interface.output_tooltip_text(&output, breadcrumb_network_path); @@ -1876,25 +1878,29 @@ impl<'a> MessageHandler> for NodeG y: position.y as i32, }; responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text }); + self.hovering_output = true; } } else if let Some(node_id) = network_interface.node_from_click(ipp.mouse.position, breadcrumb_network_path) { let text = network_interface.node_tooltip_text(&node_id, breadcrumb_network_path); - if let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) { - let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { - return; - }; - let position = network_metadata - .persistent_metadata - .navigation_metadata - .node_graph_to_viewport - .transform_point2(DVec2::new(position.x as f64 * 24., position.y as f64 * 24.)); + let Some(position) = network_interface.position(&node_id, breadcrumb_network_path) else { + log::error!("Could not get position from node: {node_id}"); + return; + }; + let Some(network_metadata) = network_interface.network_metadata(breadcrumb_network_path) else { + return; + }; + let position = network_metadata + .persistent_metadata + .navigation_metadata + .node_graph_to_viewport + .transform_point2(DVec2::new(position.x as f64 * 24., position.y as f64 * 24.)); - let xy = FrontendXY { - x: position.x as i32, - y: position.y as i32, - }; - responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text }); - } + let xy = FrontendXY { + x: position.x as i32, + y: position.y as i32, + }; + responses.add(FrontendMessage::UpdateTooltip { position: Some(xy), text }); + self.hovering_node = true; } } NodeGraphMessage::SetPinned { node_id, pinned } => { @@ -2655,6 +2661,9 @@ impl Default for NodeGraphMessageHandler { reordering_import: None, end_index: None, thumbnails: HashMap::new(), + hovering_input: false, + hovering_output: false, + hovering_node: false, } } } diff --git a/editor/src/messages/portfolio/document/node_graph/utility_types.rs b/editor/src/messages/portfolio/document/node_graph/utility_types.rs index 1a637758a1..9412e49b53 100644 --- a/editor/src/messages/portfolio/document/node_graph/utility_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/utility_types.rs @@ -1,185 +1,6 @@ -use graph_craft::document::NodeId; use std::borrow::Cow; -use crate::messages::portfolio::document::utility_types::network_interface::resolved_types::TypeSource; - -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum FrontendGraphDataType { - #[default] - General, - Number, - Artboard, - Graphic, - Raster, - Vector, - Color, - Gradient, - Typography, -} - -impl FrontendGraphDataType { - pub fn from_type(input: &Type) -> Self { - match TaggedValue::from_type_or_none(input) { - TaggedValue::U32(_) - | TaggedValue::U64(_) - | TaggedValue::F32(_) - | TaggedValue::F64(_) - | TaggedValue::DVec2(_) - | TaggedValue::F64Array4(_) - | TaggedValue::VecF64(_) - | TaggedValue::VecDVec2(_) - | TaggedValue::DAffine2(_) => Self::Number, - TaggedValue::Artboard(_) => Self::Artboard, - TaggedValue::Graphic(_) => Self::Graphic, - TaggedValue::Raster(_) => Self::Raster, - TaggedValue::Vector(_) => Self::Vector, - TaggedValue::Color(_) => Self::Color, - TaggedValue::Gradient(_) | TaggedValue::GradientStops(_) | TaggedValue::GradientTable(_) => Self::Gradient, - TaggedValue::String(_) => Self::Typography, - _ => Self::General, - } - } - - pub fn displayed_type(type_source: &TypeSource) -> Self { - match type_source.compiled_nested_type() { - Some(nested_type) => Self::from_type(&nested_type), - None => Self::General, - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendXY { - pub x: i32, - pub y: i32, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendGraphInput { - #[serde(rename = "dataType")] - pub data_type: FrontendGraphDataType, - #[serde(rename = "resolvedType")] - pub resolved_type: String, - pub name: String, - pub description: String, - /// Either "nothing", "import index {index}", or "{node name} output {output_index}". - #[serde(rename = "connectedToString")] - pub connected_to: String, - /// Used to render the upstream node once this node is rendered - #[serde(rename = "connectedToNode")] - pub connected_to_node: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendGraphOutput { - #[serde(rename = "dataType")] - pub data_type: FrontendGraphDataType, - pub name: String, - #[serde(rename = "resolvedType")] - pub resolved_type: String, - pub description: String, - /// If connected to an export, it is "export index {index}". - /// If connected to a node, it is "{node name} input {input_index}". - #[serde(rename = "connectedTo")] - pub connected_to: Vec, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendExport { - pub port: FrontendGraphInput, - pub wire: Option, -} - -#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendExports { - /// If the primary export is not visible, then it is None. - pub exports: Vec>, - #[serde(rename = "previewWire")] - pub preview_wire: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendImport { - pub port: FrontendGraphOutput, - pub wires: Vec, -} - -// Metadata that is common to nodes and layers -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNodeMetadata { - #[serde(rename = "nodeId")] - pub node_id: NodeId, - // TODO: Remove and replace with popup manager system - #[serde(rename = "canBeLayer")] - pub can_be_layer: bool, - #[serde(rename = "displayName")] - pub display_name: String, - pub selected: bool, - // Used to get the description, which is stored in a global hashmap - pub reference: Option, - // Reduces opacity of node/hidden eye icon - pub visible: bool, - // The svg string for each input - // pub wires: Vec>, - pub errors: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNode { - // pub position: FrontendNodePosition, - pub position: FrontendXY, - pub inputs: Vec>, - pub outputs: Vec>, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendLayer { - #[serde(rename = "bottomInput")] - pub bottom_input: FrontendGraphInput, - #[serde(rename = "sideInput")] - pub side_input: Option, - pub output: FrontendGraphOutput, - // pub position: FrontendLayerPosition, - pub position: FrontendXY, - pub locked: bool, - #[serde(rename = "chainWidth")] - pub chain_width: u32, - #[serde(rename = "layerHasLeftBorderGap")] - pub layer_has_left_border_gap: bool, - #[serde(rename = "primaryInputConnectedToLayer")] - pub primary_input_connected_to_layer: bool, - #[serde(rename = "primaryOutputConnectedToLayer")] - pub primary_output_connected_to_layer: bool, -} - -// // Should be an enum but those are hard to serialize/deserialize to TS -// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -// pub struct FrontendNodePosition { -// pub absolute: Option, -// pub chain: Option, -// } - -// // Should be an enum but those are hard to serialize/deserialize to TS -// #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -// pub struct FrontendLayerPosition { -// pub absolute: Option, -// pub stack: Option, -// } - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNodeOrLayer { - pub node: Option, - pub layer: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] -pub struct FrontendNodeToRender { - pub metadata: FrontendNodeMetadata, - #[serde(rename = "nodeOrLayer")] - pub node_or_layer: FrontendNodeOrLayer, - //TODO: Remove - pub wires: Vec<(String, bool, FrontendGraphDataType)>, -} +use graphene_std::uuid::NodeId; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] pub struct FrontendNodeType { 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 0a0728df77..1ca6173b0f 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -2675,6 +2675,16 @@ impl NodeNetworkInterface { self.unload_modify_import_export(network_path); } + pub fn set_node_graph_width(&mut self, node_graph_width: f64, network_path: &[NodeId]) { + let Some(network_metadata) = self.network_metadata_mut(network_path) else { + log::error!("Could not get nested network in set_transform"); + return; + }; + network_metadata.persistent_metadata.navigation_metadata.node_graph_width = node_graph_width; + self.unload_import_export_ports(network_path); + self.unload_modify_import_export(network_path); + } + pub fn vector_modify(&mut self, node_id: &NodeId, modification_type: VectorModificationType) { let Some(node) = self.network_mut(&[]).unwrap().nodes.get_mut(node_id) else { log::error!("Could not get node in vector_modification"); @@ -5853,7 +5863,7 @@ pub enum LayerClickTargetTypes { // Preview, } -#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)] pub struct NavigationMetadata { /// The current pan, and zoom state of the viewport's view of the node graph. /// Ensure `DocumentMessage::UpdateDocumentTransform` is called when the pan, zoom, or transform changes. @@ -5861,21 +5871,10 @@ pub struct NavigationMetadata { // TODO: Remove and replace with calculate_offset_transform from the node_graph_ptz. This will be difficult since it requires both the navigation message handler and the IPP /// Transform from node graph space to viewport space. pub node_graph_to_viewport: DAffine2, - /// Top right of the node graph in viewport space + // TODO: Eventually replace with footprint + /// The width of the node graph in viewport space #[serde(default)] - pub node_graph_top_right: DVec2, -} - -impl Default for NavigationMetadata { - fn default() -> NavigationMetadata { - // Default PTZ and transform - NavigationMetadata { - node_graph_ptz: PTZ::default(), - node_graph_to_viewport: DAffine2::IDENTITY, - // TODO: Eventually replace with footprint - node_graph_top_right: DVec2::ZERO, - } - } + pub node_graph_width: f64, } // PartialEq required by message handlers diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs index 67e3073786..c508dffc16 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/node_graph.rs @@ -1,19 +1,19 @@ use glam::{DVec2, IVec2}; use graph_craft::proto::GraphErrors; -use graphene_std::uuid::NodeId; +use graphene_std::{ + node_graph_overlay::types::{ + FrontendExport, FrontendExports, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, FrontendNodeToRender, + FrontendXY, + }, + uuid::NodeId, +}; use kurbo::BezPath; use crate::{ consts::{EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP, EXPORTS_TO_TOP_EDGE_PIXEL_GAP, GRID_SIZE, IMPORTS_TO_LEFT_EDGE_PIXEL_GAP, IMPORTS_TO_TOP_EDGE_PIXEL_GAP}, - messages::portfolio::document::{ - node_graph::utility_types::{ - FrontendExport, FrontendExports, FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendImport, FrontendLayer, FrontendNode, FrontendNodeMetadata, FrontendNodeOrLayer, - FrontendNodeToRender, FrontendXY, - }, - utility_types::{ - network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing}, - wires::{GraphWireStyle, build_vector_wire}, - }, + messages::portfolio::document::utility_types::{ + network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector, Previewing}, + wires::{GraphWireStyle, build_vector_wire}, }, }; @@ -123,7 +123,7 @@ impl NodeNetworkInterface { ( wire, self.wire_is_thick(&InputConnector::node(node_id, input_index), network_path), - FrontendGraphDataType::displayed_type(&self.input_type(&InputConnector::node(node_id, input_index), network_path)), + self.input_type(&InputConnector::node(node_id, input_index), network_path).displayed_type(), ) }) }) @@ -210,8 +210,12 @@ impl NodeNetworkInterface { } } }; + let connected = self + .outward_wires(network_path) + .and_then(|outward_wires| outward_wires.get(output_connector)) + .is_some_and(|downstream| downstream.len() > 0); let data_type = output_type.displayed_type(); - Some(FrontendGraphOutput { data_type, name }) + Some(FrontendGraphOutput { data_type, name, connected }) } pub fn chain_width(&self, node_id: &NodeId, network_path: &[NodeId]) -> u32 { @@ -386,19 +390,18 @@ impl NodeNetworkInterface { let import_top_left = DVec2::new(top_left_inner_bound.x.min(bounding_box_top_left.x), top_left_inner_bound.y.min(bounding_box_top_left.y)); let rounded_import_top_left = DVec2::new((import_top_left.x / 24.).round() * 24., (import_top_left.y / 24.).round() * 24.); - let viewport_top_right = network_metadata.persistent_metadata.navigation_metadata.node_graph_top_right; - let target_viewport_top_right = DVec2::new( - viewport_top_right.x - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, - viewport_top_right.y + EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64, - ); + let viewport_width = network_metadata.persistent_metadata.navigation_metadata.node_graph_width; + + let target_viewport_top_right = DVec2::new(viewport_width - EXPORTS_TO_RIGHT_EDGE_PIXEL_GAP as f64, EXPORTS_TO_TOP_EDGE_PIXEL_GAP as f64); // An offset from the right edge in viewport pixels let node_graph_pixel_offset_top_right = node_graph_to_viewport.inverse().transform_point2(target_viewport_top_right); // A 5x5 grid offset from the right corner - let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(viewport_top_right) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64); + let node_graph_grid_space_offset_top_right = node_graph_to_viewport.inverse().transform_point2(DVec2::new(viewport_width, 0.)) + DVec2::new(-5. * GRID_SIZE as f64, 4. * GRID_SIZE as f64); - // The inner bound of the export is the highest/furthest right of the two offsets + // The inner bound of the export is the highest/furthest right of the two offsets. + // When zoomed out this keeps it a constant grid space away from the edge, but when zoomed in it prevents the exports from getting too far in let top_right_inner_bound = DVec2::new( node_graph_pixel_offset_top_right.x.max(node_graph_grid_space_offset_top_right.x), node_graph_pixel_offset_top_right.y.min(node_graph_grid_space_offset_top_right.y), @@ -561,6 +564,6 @@ impl NodeNetworkInterface { return String::new(); }; - format!("{display_name}\nReference: {reference}\n\n{description}") + format!("{display_name}\n\nReference: {reference:?}\n\n{description}") } } diff --git a/editor/src/messages/portfolio/document/utility_types/wires.rs b/editor/src/messages/portfolio/document/utility_types/wires.rs index a29c84c8e8..15b5852622 100644 --- a/editor/src/messages/portfolio/document/utility_types/wires.rs +++ b/editor/src/messages/portfolio/document/utility_types/wires.rs @@ -1,5 +1,5 @@ use glam::{DVec2, IVec2}; -use graphene_std::vector::misc::dvec2_to_point; +use graphene_std::{node_graph_overlay::types::FrontendGraphDataType, vector::misc::dvec2_to_point}; use kurbo::{BezPath, DEFAULT_ACCURACY, Line, Point, Shape}; #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 66a27b84c3..3ae2f57f67 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -562,7 +562,7 @@ struct PathToolData { saved_selection_before_handle_drag: HashMap, HashSet)>, handle_drag_toggle: bool, saved_points_before_anchor_convert_smooth_sharp: HashMap>, - last_click_time: Instant, + last_click_time: u64, dragging_state: DraggingState, angle: f64, pivot_gizmo: PivotGizmo, diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 19ffb07c59..37e08bbd38 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -12,6 +12,7 @@ use graphene_std::text::FontCache; use graphene_std::transform::Footprint; use graphene_std::vector::Vector; use graphene_std::wasm_application_io::RenderOutputType; +use graphene_std::Graphic; use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta; mod runtime_io; @@ -121,14 +122,7 @@ impl NodeGraphExecutor { /// Update the cached network if necessary. fn update_node_graph(&mut self, document: &mut DocumentMessageHandler, node_to_inspect: Option, ignore_hash: bool) -> Result<(), String> { - let mut network = document.network_interface.document_network().clone(); - if let Some(mut node_graph_overlay_node) = document.node_graph_handler.node_graph_overlay.clone() { - let node_graph_overlay_id = NodeId::new(); - let new_export = NodeInput::node(node_graph_overlay_id, 0); - let old_export = std::mem::replace(&mut network.exports[0], new_export); - node_graph_overlay_node.inputs[0] = old_export; - network.nodes.insert(node_graph_overlay_id, node_graph_overlay_node); - } + let network = document.network_interface.document_network().clone(); let network_hash = network.current_hash(); // Refresh the graph when it changes or the inspect node changes if network_hash != self.node_graph_hash || self.previous_node_to_inspect != node_to_inspect || ignore_hash { diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 985f317d6c..35609d00d5 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -299,7 +299,7 @@ style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24} style:--offset-top={($nodeGraph.updateImportsExports.importPosition.y - 8) / 24 + index} > - {#if frontendOutput.connectedTo.length > 0} + {#if frontendOutput.connected} {:else} diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index 6da2dddcda..d906ad7e8f 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -157,8 +157,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli // TODO: This would allow it to properly decide to act on removing hover focus from something that was hovered in the canvas before moving over the GUI. // TODO: Further explanation: https://github.com/GraphiteEditor/Graphite/pull/623#discussion_r866436197 const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]"); - const inGraphOverlay = get(document).graphViewOverlayOpen; - if (!viewportPointerInteractionOngoing && (inFloatingMenu || inGraphOverlay)) return; + if (!viewportPointerInteractionOngoing && inFloatingMenu) return; const modifiers = makeKeyboardModifiersBitfield(e); if (detectShake(e)) editor.handle.onMouseShake(e.clientX, e.clientY, e.buttons, modifiers); diff --git a/frontend/src/messages.ts b/frontend/src/messages.ts index b4d76fd870..a07fcaaed9 100644 --- a/frontend/src/messages.ts +++ b/frontend/src/messages.ts @@ -127,17 +127,16 @@ export class UpdateNodeThumbnail extends JsMessage { readonly value!: string; } +export class UpdateOpenDocumentsList extends JsMessage { + @Type(() => OpenDocument) + readonly openDocuments!: OpenDocument[]; +} export class UpdateTooltip extends JsMessage { readonly position!: XY | undefined; readonly text!: string; } -export class UpdateOpenDocumentsList extends JsMessage { - @Type(() => FrontendDocumentDetails) - readonly openDocuments!: FrontendDocumentDetails[]; -} - export class WirePathInProgress { readonly wire!: string; readonly thick!: boolean; @@ -148,35 +147,28 @@ export class UpdateWirePathInProgress extends JsMessage { readonly wirePathInProgress!: WirePathInProgress | undefined; } -// Allows the auto save system to use a string for the id rather than a BigInt. -// IndexedDb does not allow for BigInts as primary keys. -// TypeScript does not allow subclasses to change the type of class variables in subclasses. -// It is an abstract class to point out that it should not be instantiated directly. -export abstract class DocumentDetails { +export class OpenDocument { + readonly id!: bigint; + @Type(() => DocumentDetails) + readonly details!: DocumentDetails; + + get displayName(): string { + return this.details.displayName; + } +} + +export class DocumentDetails { readonly name!: string; readonly isAutoSaved!: boolean; readonly isSaved!: boolean; - // This field must be provided by the subclass implementation - // readonly id!: bigint | string; - get displayName(): string { return `${this.name}${this.isSaved ? "" : "*"}`; } } -export class FrontendDocumentDetails extends DocumentDetails { - readonly id!: bigint; -} - -======= -export class FrontendDocumentDetails extends DocumentDetails { - readonly id!: bigint; -} - ->>>>>>> 17a1a3d5 (Complete separating node rendering from imports/exports) export type FrontendGraphDataType = "General" | "Number" | "Artboard" | "Graphic" | "Raster" | "Vector" | "Color"; export class FrontendGraphInput { @@ -198,11 +190,7 @@ export class FrontendGraphOutput { readonly name!: string; - readonly description!: string; - - readonly resolvedType!: string; - - readonly connectedTo!: string[]; + readonly connected!: boolean; } export class FrontendExport { @@ -322,21 +310,20 @@ export class WireUpdate { readonly wirePathUpdate!: WirePath | undefined; } -export class IndexedDbDocumentDetails extends DocumentDetails { +export class TriggerPersistenceWriteDocument extends JsMessage { + // Use a string since IndexedDB can not use BigInts for keys @Transform(({ value }: { value: bigint }) => value.toString()) - id!: string; -} + documentId!: string; -export class TriggerIndexedDbWriteDocument extends JsMessage { document!: string; - @Type(() => IndexedDbDocumentDetails) - details!: IndexedDbDocumentDetails; + @Type(() => DocumentDetails) + details!: DocumentDetails; version!: string; } -export class TriggerIndexedDbRemoveDocument extends JsMessage { +export class TriggerPersistenceRemoveDocument extends JsMessage { // Use a string since IndexedDB can not use BigInts for keys @Transform(({ value }: { value: bigint }) => value.toString()) documentId!: string; @@ -1469,7 +1456,6 @@ export class WidgetDiffUpdate extends JsMessage { layoutTarget!: unknown; // TODO: Replace `any` with correct typing - @Transform(({ value }: { value: any }) => createWidgetDiff(value)) diff!: WidgetDiff[]; } @@ -1504,7 +1490,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg return targetLayout; } // This is a path traversal so we can assume from the backend that it exists - if (targetLayout && "action" in targetLayout) return targetLayout.children![index]; return targetLayout?.[index]; @@ -1525,7 +1510,6 @@ export function patchWidgetLayout(layout: /* &mut */ WidgetLayout, updates: Widg diffObject.length = 0; } // Remove all of the keys from the old object - Object.keys(diffObject).forEach((key) => delete (diffObject as any)[key]); // Assign keys to the new object @@ -1558,7 +1542,6 @@ export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSect } // Unpacking rust types to more usable type in the frontend - function createWidgetDiff(diffs: any[]): WidgetDiff[] { return diffs.map((diff) => { const { widgetPath, newValue } = diff; @@ -1577,7 +1560,6 @@ function createWidgetDiff(diffs: any[]): WidgetDiff[] { } // Unpacking a layout group - function createLayoutGroup(layoutGroup: any): LayoutGroup { if (layoutGroup.column) { const columnWidgets = hoistWidgetHolders(layoutGroup.column.columnWidgets); @@ -1635,7 +1617,6 @@ export class UpdateMenuBarLayout extends JsMessage { layoutTarget!: unknown; // TODO: Replace `any` with correct typing - @Transform(({ value }: { value: any }) => createMenuLayout(value)) layout!: MenuBarEntry[]; } @@ -1658,7 +1639,6 @@ function createMenuLayout(menuBarEntry: any[]): MenuBarEntry[] { children: createMenuLayoutRecursive(entry.children), })); } - function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] { return children.map((groups) => groups.map((entry) => ({ @@ -1671,7 +1651,6 @@ function createMenuLayoutRecursive(children: any[][]): MenuBarEntry[][] { } // `any` is used since the type of the object should be known from the Rust side - type JSMessageFactory = (data: any, wasm: WebAssembly.Memory, handle: EditorHandle) => JsMessage; type MessageMaker = typeof JsMessage | JSMessageFactory; @@ -1691,8 +1670,8 @@ export const messageMakers: Record = { TriggerFetchAndOpenDocument, TriggerFontLoad, TriggerImport, - TriggerIndexedDbRemoveDocument, - TriggerIndexedDbWriteDocument, + TriggerPersistenceRemoveDocument, + TriggerPersistenceWriteDocument, TriggerLoadFirstAutoSaveDocument, TriggerLoadPreferences, TriggerLoadRestAutoSaveDocuments, diff --git a/frontend/src/state-providers/node-graph.ts b/frontend/src/state-providers/node-graph.ts index 74762825df..2c40a589de 100644 --- a/frontend/src/state-providers/node-graph.ts +++ b/frontend/src/state-providers/node-graph.ts @@ -5,21 +5,21 @@ import { type FrontendSelectionBox, type FrontendClickTargets, type ContextMenuInformation, - type FrontendNodeToRender, type FrontendNodeType, type WirePathInProgress, + type XY, SendUIMetadata, UpdateClickTargets, UpdateContextMenuInformation, UpdateImportReorderIndex, UpdateExportReorderIndex, UpdateImportsExports, - UpdateLayerWidths, UpdateNativeNodeGraphSVG, UpdateNodeThumbnail, UpdateWirePathInProgress, UpdateNodeGraphSelectionBox, UpdateNodeGraphTransform, + UpdateTooltip, } from "@graphite/messages"; export function createNodeGraphState(editor: Editor) { @@ -37,11 +37,8 @@ export function createNodeGraphState(editor: Editor) { nodeTypes: [] as FrontendNodeType[], nodeDescriptions: new Map(), - // Data that will be moved into the node graph to be rendered natively - nodesToRender: new Map(), - opacity: 0.8, - inSelectedNetwork: true, - previewedNode: undefined as bigint | undefined, + tooltipPosition: undefined as XY | undefined, + tooltipText: "test", // Data that will be passed in the context thumbnails: new Map(), @@ -117,7 +114,13 @@ export function createNodeGraphState(editor: Editor) { return state; }); }); - + editor.subscriptions.subscribeJsMessage(UpdateTooltip, (updateTooltip) => { + update((state) => { + state.tooltipPosition = updateTooltip.position; + state.tooltipText = updateTooltip.text; + return state; + }); + }); return { subscribe, }; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 761d87f97a..fcb6dae1d5 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -5,11 +5,8 @@ // on the dispatcher messaging system and more complex Rust data types. // use crate::helpers::translate_key; -#[cfg(not(feature = "native"))] -use crate::wasm_node_graph_ui_executor::WasmNodeGraphUIExecutor; -use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER, WASM_NODE_GRAPH_EXECUTOR}; +use crate::{EDITOR_HANDLE, EDITOR_HAS_CRASHED, Error, MESSAGE_BUFFER}; use editor::consts::FILE_EXTENSION; -use editor::dispatcher::EditorOutput; use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds}; use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; @@ -175,13 +172,9 @@ impl EditorHandle { pub fn new(frontend_message_handler_callback: js_sys::Function) -> Self { let editor = Editor::new(); let editor_handle = EditorHandle { frontend_message_handler_callback }; - let node_graph_executor = WasmNodeGraphUIExecutor::new(); if EDITOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor))).is_none() { log::error!("Attempted to initialize the editor more than once"); } - if WASM_NODE_GRAPH_EXECUTOR.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(node_graph_executor))).is_none() { - log::error!("Attempted to initialize the editor more than once"); - } if EDITOR_HANDLE.with(|handle| handle.lock().ok().map(|mut guard| *guard = Some(editor_handle.clone()))).is_none() { log::error!("Attempted to initialize the editor handle more than once"); } @@ -202,12 +195,28 @@ impl EditorHandle { #[cfg(not(feature = "native"))] fn dispatch>(&self, message: T) { // Process no further messages after a crash to avoid spamming the console + + use crate::MESSAGE_BUFFER; if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { return; } - let _ = editor(|editor| { - self.process_messages(std::iter::once(message.into()), editor); + + // Get the editor, dispatch the message, and store the `FrontendMessage` queue response + let frontend_messages = EDITOR.with(|editor| { + let mut guard = editor.try_lock(); + let Ok(Some(editor)) = guard.as_deref_mut() else { + // Enqueue messages which can't be procssed currently + MESSAGE_BUFFER.with_borrow_mut(|buffer| buffer.push(message.into())); + return vec![]; + }; + + editor.handle_message(message) }); + + // Send each `FrontendMessage` to the JavaScript frontend + for message in frontend_messages.into_iter() { + self.send_frontend_message_to_js(message); + } } #[cfg(feature = "native")] @@ -220,37 +229,6 @@ impl EditorHandle { crate::native_communcation::send_message_to_cef(serialized_message) } - // Messages can come from the runtime, browser, or a timed callback. This processes them in the editor and does all the side effects - // Like updating the frontend and node graph ui network. Some side effects are deduplicated and produce other side effects. - fn process_messages(&self, messages: impl IntoIterator, editor_param: &mut Editor) { - // Get the editor, dispatch the message, and store the `FrontendMessage` queue response - for output in messages.into_iter().flat_map(|message| editor_param.handle_message(message)).collect::>() { - match output { - EditorOutput::RequestNativeNodeGraphRender { compilation_request } => { - let res = executor(|executor| executor.compilation_request(compilation_request)); - if let Err(_) = res { - log::error!("Could not borrow executor in process_messages_in_editor"); - } - } - EditorOutput::RequestDeferredMessage { message, timeout } => { - let callback = Closure::once_into_js(move || { - editor_and_handle(|editor, handle| { - handle.process_messages(std::iter::once(*message), editor); - }); - }); - - window() - .unwrap() - .set_timeout_with_callback_and_timeout_and_arguments_0(callback.as_ref().unchecked_ref(), timeout.as_millis() as i32) - .unwrap(); - } - EditorOutput::FrontendMessage { frontend_message } => { - self.send_frontend_message_to_js(frontend_message); - } - } - } - } - // ======================================================================== // Add additional JS -> Rust wrapper functions below as needed for calling // the backend from the web frontend. @@ -280,20 +258,6 @@ impl EditorHandle { #[cfg(not(feature = "native"))] wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation()); - // Poll the UI node graph - #[cfg(not(feature = "native"))] - let result = editor(|editor| { - let node_graph_response = executor(|executor| executor.poll_node_graph_ui_evaluation(editor)); - - match node_graph_response { - Ok(node_graph_ui_messages) => handle(|handle| handle.process_messages(node_graph_ui_messages, editor)), - Err(_) => log::error!("Could not get executor in frame loop"), - } - }); - - if let Err(_) = result { - log::error!("Could not get editor in frame loop"); - } if !EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { handle(|handle| { // Process all messages that have been queued up @@ -496,6 +460,7 @@ impl EditorHandle { document_is_saved, document_serialized_content, to_front, + select_after_open: false, }; self.dispatch(message); } @@ -1000,49 +965,39 @@ fn set_timeout(f: &Closure, delay: Duration) { /// Provides access to the `Editor` by calling the given closure with it as an argument. #[cfg(not(feature = "native"))] -fn editor(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> Result { +fn editor(callback: impl FnOnce(&mut editor::application::Editor) -> T) -> T { EDITOR.with(|editor| { let mut guard = editor.try_lock(); let Ok(Some(editor)) = guard.as_deref_mut() else { - return Err(()); - }; - Ok(callback(editor)) - }) -} - -#[cfg(not(feature = "native"))] -fn executor(callback: impl FnOnce(&mut WasmNodeGraphUIExecutor) -> T) -> Result { - WASM_NODE_GRAPH_EXECUTOR.with(|executor| { - let mut guard = executor.try_lock(); - let Ok(Some(executor)) = guard.as_deref_mut() else { - return Err(()); + log::error!("Failed to borrow editor"); + return T::default(); }; - Ok(callback(executor)) - }) -} - -/// Provides access to the `EditorHandle` by calling the given closure with them as arguments. -pub(crate) fn handle(callback: impl FnOnce(&mut EditorHandle)) { - EDITOR_HANDLE.with(|editor_handle| { - let mut guard = editor_handle.try_lock(); - let Ok(Some(editor_handle)) = guard.as_deref_mut() else { - return log::error!("Failed to borrow handle"); - }; - - // Call the closure with the editor and its handle - callback(editor_handle) + callback(editor) }) } /// Provides access to the `Editor` and its `EditorHandle` by calling the given closure with them as arguments. #[cfg(not(feature = "native"))] pub(crate) fn editor_and_handle(callback: impl FnOnce(&mut Editor, &mut EditorHandle)) { - let _ = handle(|editor_handle| { - let _ = editor(|editor| { + handle(|editor_handle| { + editor(|editor| { // Call the closure with the editor and its handle callback(editor, editor_handle); - }); + }) + }); +} +/// Provides access to the `EditorHandle` by calling the given closure with them as arguments. +pub(crate) fn handle(callback: impl FnOnce(&mut EditorHandle)) { + EDITOR_HANDLE.with(|editor_handle| { + let mut guard = editor_handle.try_lock(); + let Ok(Some(editor_handle)) = guard.as_deref_mut() else { + log::error!("Failed to borrow editor handle"); + return; + }; + + // Call the closure with the editor and its handle + callback(editor_handle); }); } @@ -1071,11 +1026,15 @@ async fn poll_node_graph_evaluation() { crate::NODE_GRAPH_ERROR_DISPLAYED.store(false, Ordering::SeqCst); } - handle.process_messages(messages, editor); + // Send each `FrontendMessage` to the JavaScript frontend + for response in messages.into_iter().flat_map(|message| editor.handle_message(message)) { + handle.send_frontend_message_to_js(response); + } // If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches }); } + fn auto_save_all_documents() { // Process no further messages after a crash to avoid spamming the console if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { diff --git a/frontend/wasm/src/lib.rs b/frontend/wasm/src/lib.rs index d3141288b3..61158d7097 100644 --- a/frontend/wasm/src/lib.rs +++ b/frontend/wasm/src/lib.rs @@ -61,7 +61,7 @@ pub fn panic_hook(info: &panic::PanicHookInfo) { /text>"# // It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed. .to_string(); - handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::UpdateDocumentArtwork { svg: error }); + handle.send_frontend_message_to_js(FrontendMessage::UpdateDocumentArtwork { svg: error }); }); } @@ -75,7 +75,7 @@ pub fn panic_hook(info: &panic::PanicHookInfo) { EDITOR_HANDLE.with(|editor_handle| { let mut guard = editor_handle.lock(); if let Ok(Some(handle)) = guard.as_deref_mut() { - handle.send_frontend_message_to_js_rust_proxy(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() }); + handle.send_frontend_message_to_js(FrontendMessage::DisplayDialogPanic { panic_info: info.to_string() }); } }); } diff --git a/frontend/wasm/src/native_communcation.rs b/frontend/wasm/src/native_communcation.rs index 97cfd4863d..5803de77c7 100644 --- a/frontend/wasm/src/native_communcation.rs +++ b/frontend/wasm/src/native_communcation.rs @@ -11,7 +11,7 @@ pub fn receive_native_message(buffer: ArrayBuffer) { Ok(messages) => { let callback = move |handle: &mut EditorHandle| { for message in messages { - handle.send_frontend_message_to_js_rust_proxy(message); + handle.send_frontend_message_to_js(message); } }; editor_api::handle(callback); diff --git a/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs b/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs index b54e2e329a..9a1130c62d 100644 --- a/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs +++ b/node-graph/gcore/src/node_graph_overlay/nodes_and_wires.rs @@ -162,13 +162,7 @@ pub fn draw_nodes(nodes: &Vec) -> Table { } } - // for text_row in node_text.iter_mut() { - // text_row.element.style.fill = Fill::Solid(Color::WHITE); - // } - let node_text_row = TableRow::new_from_element(Graphic::Vector(node_text)); - // node_text_row.transform.left_apply_transform(&DAffine2::from_translation(DVec2::new(x + 8., y + 8.))); - // log::debug!("node_text_row {:?}", node_text_row.transform); node_table.push(node_text_row); // Add black clipping path to view text in node @@ -193,12 +187,12 @@ pub fn draw_nodes(nodes: &Vec) -> Table { ports_table.push(row); } if let Some(primary_output) = &frontend_node.primary_output { - let mut row = port_row(&primary_output.data_type, true); + let mut row = port_row(&primary_output.data_type, primary_output.connected); row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12.)); ports_table.push(row); } for (index, secondary_output) in frontend_node.secondary_outputs.iter().enumerate() { - let mut row = port_row(&secondary_output.data_type, true); + let mut row = port_row(&secondary_output.data_type, secondary_output.connected); row.transform = DAffine2::from_translation(DVec2::new(5. * GRID_SIZE, 12. + GRID_SIZE * (index + 1) as f64)); ports_table.push(row); } @@ -345,7 +339,12 @@ pub fn draw_layers(nodes: &mut NodeGraphOverlayData) -> (Table, Table table.to_graphic(), Graphic::Color(table) => table.to_graphic(), Graphic::Gradient(table) => table.to_graphic(), + Graphic::Typography(table) => table.to_graphic(), } } @@ -1696,6 +1697,10 @@ impl Render for Table { } } } + + fn to_graphic(self) -> Graphic { + Graphic::Typography(self) + } } #[derive(Debug, Clone, PartialEq, Eq)]