mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-09 18:30:55 +08:00
Consolidate frontend node
This commit is contained in:
@@ -41,6 +41,7 @@ impl DispatcherMessageHandlers {
|
|||||||
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
/// The last occurrence of the message in the message queue is sufficient to ensure correct behavior.
|
||||||
/// In addition, these messages do not change any state in the backend (aside from caches).
|
/// In addition, these messages do not change any state in the backend (aside from caches).
|
||||||
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
|
||||||
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::NodeGraph(NodeGraphMessageDiscriminant::SendGraph))),
|
||||||
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::PropertiesPanel(
|
||||||
PropertiesPanelMessageDiscriminant::Refresh,
|
PropertiesPanelMessageDiscriminant::Refresh,
|
||||||
))),
|
))),
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ use super::utility_types::{DocumentDetails, MouseCursorIcon, OpenDocument};
|
|||||||
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
use crate::messages::app_window::app_window_message_handler::AppWindowPlatform;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{
|
use crate::messages::portfolio::document::node_graph::utility_types::{
|
||||||
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, Transform,
|
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, FrontendXY, Transform,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::HintData;
|
use crate::messages::tool::utility_types::HintData;
|
||||||
use glam::IVec2;
|
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graphene_std::raster::Image;
|
use graphene_std::raster::Image;
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
@@ -133,18 +132,14 @@ pub enum FrontendMessage {
|
|||||||
exports: Vec<Option<FrontendGraphInput>>,
|
exports: Vec<Option<FrontendGraphInput>>,
|
||||||
/// The primary import location.
|
/// The primary import location.
|
||||||
#[serde(rename = "importPosition")]
|
#[serde(rename = "importPosition")]
|
||||||
import_position: IVec2,
|
import_position: FrontendXY,
|
||||||
/// The primary export location.
|
/// The primary export location.
|
||||||
#[serde(rename = "exportPosition")]
|
#[serde(rename = "exportPosition")]
|
||||||
export_position: IVec2,
|
export_position: FrontendXY,
|
||||||
/// The document network does not have an add import or export button.
|
/// The document network does not have an add import or export button.
|
||||||
#[serde(rename = "addImportExport")]
|
#[serde(rename = "addImportExport")]
|
||||||
add_import_export: bool,
|
add_import_export: bool,
|
||||||
},
|
},
|
||||||
UpdateInSelectedNetwork {
|
|
||||||
#[serde(rename = "inSelectedNetwork")]
|
|
||||||
in_selected_network: bool,
|
|
||||||
},
|
|
||||||
UpdateBox {
|
UpdateBox {
|
||||||
#[serde(rename = "box")]
|
#[serde(rename = "box")]
|
||||||
box_selection: Option<BoxSelection>,
|
box_selection: Option<BoxSelection>,
|
||||||
@@ -185,10 +180,6 @@ pub enum FrontendMessage {
|
|||||||
UpdateLayerWidths {
|
UpdateLayerWidths {
|
||||||
#[serde(rename = "layerWidths")]
|
#[serde(rename = "layerWidths")]
|
||||||
layer_widths: HashMap<NodeId, u32>,
|
layer_widths: HashMap<NodeId, u32>,
|
||||||
#[serde(rename = "chainWidths")]
|
|
||||||
chain_widths: HashMap<NodeId, u32>,
|
|
||||||
#[serde(rename = "hasLeftInputWire")]
|
|
||||||
has_left_input_wire: HashMap<NodeId, bool>,
|
|
||||||
},
|
},
|
||||||
UpdateDialogButtons {
|
UpdateDialogButtons {
|
||||||
#[serde(rename = "layoutTarget")]
|
#[serde(rename = "layoutTarget")]
|
||||||
@@ -284,7 +275,13 @@ pub enum FrontendMessage {
|
|||||||
cursor: MouseCursorIcon,
|
cursor: MouseCursorIcon,
|
||||||
},
|
},
|
||||||
UpdateNodeGraphNodes {
|
UpdateNodeGraphNodes {
|
||||||
nodes: Vec<FrontendNode>,
|
#[serde(rename = "nodesToRender")]
|
||||||
|
nodes_to_render: Vec<FrontendNode>,
|
||||||
|
#[serde(rename = "inSelectedNetwork")]
|
||||||
|
in_selected_network: bool,
|
||||||
|
// Displays a dashed border around the node
|
||||||
|
#[serde(rename = "previewedNode")]
|
||||||
|
previewed_node: Option<NodeId>,
|
||||||
},
|
},
|
||||||
UpdateVisibleNodes {
|
UpdateVisibleNodes {
|
||||||
nodes: Vec<NodeId>,
|
nodes: Vec<NodeId>,
|
||||||
@@ -298,9 +295,6 @@ pub enum FrontendMessage {
|
|||||||
layout_target: LayoutTarget,
|
layout_target: LayoutTarget,
|
||||||
diff: Vec<WidgetDiff>,
|
diff: Vec<WidgetDiff>,
|
||||||
},
|
},
|
||||||
UpdateNodeGraphSelection {
|
|
||||||
selected: Vec<NodeId>,
|
|
||||||
},
|
|
||||||
UpdateNodeGraphTransform {
|
UpdateNodeGraphTransform {
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1191,7 +1191,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
|
|||||||
}
|
}
|
||||||
responses.add(PropertiesPanelMessage::Refresh);
|
responses.add(PropertiesPanelMessage::Refresh);
|
||||||
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
||||||
responses.add(NodeGraphMessage::UpdateInSelectedNetwork);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
}
|
}
|
||||||
DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => {
|
DocumentMessage::SetBlendModeForSelectedLayers { blend_mode } => {
|
||||||
for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) {
|
for layer in self.network_interface.selected_nodes().selected_layers_except_artboards(&self.network_interface) {
|
||||||
|
|||||||
@@ -231,7 +231,5 @@ pub enum NodeGraphMessage {
|
|||||||
},
|
},
|
||||||
UpdateActionButtons,
|
UpdateActionButtons,
|
||||||
UpdateGraphBarRight,
|
UpdateGraphBarRight,
|
||||||
UpdateInSelectedNetwork,
|
|
||||||
UpdateHints,
|
UpdateHints,
|
||||||
SendSelectedNodes,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
|||||||
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
|
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType, FrontendXY};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
use crate::messages::portfolio::document::utility_types::network_interface::{
|
||||||
@@ -192,7 +192,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
||||||
responses.add(PropertiesPanelMessage::Refresh);
|
responses.add(PropertiesPanelMessage::Refresh);
|
||||||
responses.add(NodeGraphMessage::SendSelectedNodes);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
responses.add(ArtboardToolMessage::UpdateSelectedArtboard);
|
responses.add(ArtboardToolMessage::UpdateSelectedArtboard);
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
@@ -1633,21 +1633,22 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
responses.add(NodeGraphMessage::UpdateLayerPanel);
|
||||||
responses.add(DocumentMessage::DocumentStructureChanged);
|
responses.add(DocumentMessage::DocumentStructureChanged);
|
||||||
responses.add(PropertiesPanelMessage::Refresh);
|
responses.add(PropertiesPanelMessage::Refresh);
|
||||||
if breadcrumb_network_path == selection_network_path && graph_view_overlay_open {
|
responses.add(NodeGraphMessage::UpdateActionButtons);
|
||||||
let nodes = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path);
|
if graph_view_overlay_open {
|
||||||
self.frontend_nodes = nodes.iter().map(|node| node.id).collect();
|
let nodes_to_render = network_interface.collect_nodes(&self.node_graph_errors, breadcrumb_network_path);
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphNodes { nodes });
|
self.frontend_nodes = nodes_to_render.iter().map(|node| node.id).collect();
|
||||||
|
let previewed_node = network_interface.previewed_node(breadcrumb_network_path);
|
||||||
|
responses.add(FrontendMessage::UpdateNodeGraphNodes {
|
||||||
|
nodes_to_render,
|
||||||
|
in_selected_network: selection_network_path == breadcrumb_network_path,
|
||||||
|
previewed_node,
|
||||||
|
});
|
||||||
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
responses.add(NodeGraphMessage::UpdateVisibleNodes);
|
||||||
|
|
||||||
let (layer_widths, chain_widths, has_left_input_wire) = network_interface.collect_layer_widths(breadcrumb_network_path);
|
let layer_widths = network_interface.collect_layer_widths(breadcrumb_network_path);
|
||||||
|
|
||||||
responses.add(NodeGraphMessage::UpdateImportsExports);
|
responses.add(NodeGraphMessage::UpdateImportsExports);
|
||||||
responses.add(FrontendMessage::UpdateLayerWidths {
|
responses.add(FrontendMessage::UpdateLayerWidths { layer_widths });
|
||||||
layer_widths,
|
|
||||||
chain_widths,
|
|
||||||
has_left_input_wire,
|
|
||||||
});
|
|
||||||
responses.add(NodeGraphMessage::SendSelectedNodes);
|
|
||||||
responses.add(NodeGraphMessage::SendWires);
|
responses.add(NodeGraphMessage::SendWires);
|
||||||
self.update_node_graph_hints(responses);
|
self.update_node_graph_hints(responses);
|
||||||
}
|
}
|
||||||
@@ -1956,6 +1957,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let import_position = FrontendXY {
|
||||||
|
x: import_position.x,
|
||||||
|
y: import_position.y,
|
||||||
|
};
|
||||||
|
let export_position = FrontendXY {
|
||||||
|
x: export_position.x,
|
||||||
|
y: export_position.y,
|
||||||
|
};
|
||||||
|
|
||||||
// Do not show the add import or add export button in the document network;
|
// Do not show the add import or add export button in the document network;
|
||||||
let add_import_export = !breadcrumb_network_path.is_empty();
|
let add_import_export = !breadcrumb_network_path.is_empty();
|
||||||
|
|
||||||
@@ -2000,22 +2010,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
|
|||||||
self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler);
|
self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler);
|
||||||
self.send_node_bar_layout(responses);
|
self.send_node_bar_layout(responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::UpdateInSelectedNetwork => responses.add(FrontendMessage::UpdateInSelectedNetwork {
|
|
||||||
in_selected_network: selection_network_path == breadcrumb_network_path,
|
|
||||||
}),
|
|
||||||
NodeGraphMessage::UpdateHints => {
|
NodeGraphMessage::UpdateHints => {
|
||||||
self.update_node_graph_hints(responses);
|
self.update_node_graph_hints(responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SendSelectedNodes => {
|
|
||||||
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(breadcrumb_network_path) else {
|
|
||||||
log::error!("Could not get selected nodes in NodeGraphMessage::SendSelectedNodes");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
responses.add(NodeGraphMessage::UpdateActionButtons);
|
|
||||||
responses.add(FrontendMessage::UpdateNodeGraphSelection {
|
|
||||||
selected: selected_nodes.selected_nodes().cloned().collect::<Vec<_>>(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
||||||
log::error!("Could not get selected nodes in NodeGraphMessageHandler");
|
log::error!("Could not get selected nodes in NodeGraphMessageHandler");
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use glam::IVec2;
|
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::Type;
|
use graphene_std::Type;
|
||||||
@@ -51,6 +50,12 @@ impl FrontendGraphDataType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct FrontendGraphInput {
|
pub struct FrontendGraphInput {
|
||||||
#[serde(rename = "dataType")]
|
#[serde(rename = "dataType")]
|
||||||
@@ -85,6 +90,7 @@ pub struct FrontendNode {
|
|||||||
pub is_layer: bool,
|
pub is_layer: bool,
|
||||||
#[serde(rename = "canBeLayer")]
|
#[serde(rename = "canBeLayer")]
|
||||||
pub can_be_layer: bool,
|
pub can_be_layer: bool,
|
||||||
|
pub selected: bool,
|
||||||
pub reference: Option<String>,
|
pub reference: Option<String>,
|
||||||
#[serde(rename = "displayName")]
|
#[serde(rename = "displayName")]
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
@@ -96,11 +102,15 @@ pub struct FrontendNode {
|
|||||||
pub primary_output: Option<FrontendGraphOutput>,
|
pub primary_output: Option<FrontendGraphOutput>,
|
||||||
#[serde(rename = "exposedOutputs")]
|
#[serde(rename = "exposedOutputs")]
|
||||||
pub exposed_outputs: Vec<FrontendGraphOutput>,
|
pub exposed_outputs: Vec<FrontendGraphOutput>,
|
||||||
|
#[serde(rename = "chainWidth")]
|
||||||
|
pub chain_width: u32,
|
||||||
|
#[serde(rename = "layerHasLeftBorderGap")]
|
||||||
|
pub layer_has_left_border_gap: bool,
|
||||||
#[serde(rename = "primaryOutputConnectedToLayer")]
|
#[serde(rename = "primaryOutputConnectedToLayer")]
|
||||||
pub primary_output_connected_to_layer: bool,
|
pub primary_output_connected_to_layer: bool,
|
||||||
#[serde(rename = "primaryInputConnectedToLayer")]
|
#[serde(rename = "primaryInputConnectedToLayer")]
|
||||||
pub primary_input_connected_to_layer: bool,
|
pub primary_input_connected_to_layer: bool,
|
||||||
pub position: IVec2,
|
pub position: FrontendXY,
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
pub locked: bool,
|
pub locked: bool,
|
||||||
pub previewed: bool,
|
pub previewed: bool,
|
||||||
|
|||||||
@@ -2853,10 +2853,11 @@ impl NodeNetworkInterface {
|
|||||||
bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport)
|
bounding_box_subpath.bounding_box_with_transform(network_metadata.persistent_metadata.navigation_metadata.node_graph_to_viewport)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> (HashMap<NodeId, u32>, HashMap<NodeId, u32>, HashMap<NodeId, bool>) {
|
// TODO: Remove and get layer click targets from render output
|
||||||
|
pub fn collect_layer_widths(&mut self, network_path: &[NodeId]) -> HashMap<NodeId, u32> {
|
||||||
let Some(network_metadata) = self.network_metadata(network_path) else {
|
let Some(network_metadata) = self.network_metadata(network_path) else {
|
||||||
log::error!("Could not get nested network_metadata in collect_layer_widths");
|
log::error!("Could not get nested network_metadata in collect_layer_widths");
|
||||||
return (HashMap::new(), HashMap::new(), HashMap::new());
|
return HashMap::new();
|
||||||
};
|
};
|
||||||
let nodes = network_metadata
|
let nodes = network_metadata
|
||||||
.persistent_metadata
|
.persistent_metadata
|
||||||
@@ -2868,13 +2869,8 @@ impl NodeNetworkInterface {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width)))
|
.filter_map(|node_id| self.layer_width(node_id, network_path).map(|layer_width| (*node_id, layer_width)))
|
||||||
.collect::<HashMap<NodeId, u32>>();
|
.collect::<HashMap<NodeId, u32>>();
|
||||||
let chain_widths = nodes.iter().map(|node_id| (*node_id, self.chain_width(node_id, network_path))).collect::<HashMap<NodeId, u32>>();
|
|
||||||
let has_left_input_wire = nodes
|
|
||||||
.iter()
|
|
||||||
.map(|node_id| (*node_id, self.layer_has_left_border_gap(node_id, network_path)))
|
|
||||||
.collect::<HashMap<NodeId, bool>>();
|
|
||||||
|
|
||||||
(layer_widths, chain_widths, has_left_input_wire)
|
layer_widths
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
|
pub fn compute_modified_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
|
||||||
|
|||||||
+19
-17
@@ -5,7 +5,7 @@ use graphene_std::uuid::NodeId;
|
|||||||
use crate::{
|
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},
|
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::{
|
messages::portfolio::document::{
|
||||||
node_graph::utility_types::{FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendNode},
|
node_graph::utility_types::{FrontendGraphDataType, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendXY},
|
||||||
utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector},
|
utility_types::network_interface::{FlowType, InputConnector, NodeNetworkInterface, OutputConnector},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -17,6 +17,7 @@ impl NodeNetworkInterface {
|
|||||||
log::error!("Could not get nested network when collecting nodes");
|
log::error!("Could not get nested network when collecting nodes");
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
|
let selected_nodes = self.selected_nodes_in_nested_network(network_path).unwrap_or_default();
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::<Vec<_>>() {
|
for (node_id, visible) in network.nodes.iter().map(|(node_id, node)| (*node_id, node.visible)).collect::<Vec<_>>() {
|
||||||
let node_id_path = [network_path, &[node_id]].concat();
|
let node_id_path = [network_path, &[node_id]].concat();
|
||||||
@@ -37,19 +38,12 @@ impl NodeNetworkInterface {
|
|||||||
let exposed_outputs = (1..self.number_of_outputs(&node_id, network_path))
|
let exposed_outputs = (1..self.number_of_outputs(&node_id, network_path))
|
||||||
.filter_map(|output_index| self.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), network_path))
|
.filter_map(|output_index| self.frontend_output_from_connector(&OutputConnector::node(node_id, output_index), network_path))
|
||||||
.collect();
|
.collect();
|
||||||
let (primary_output_connected_to_layer, primary_input_connected_to_layer) = if self.is_layer(&node_id, network_path) {
|
|
||||||
(
|
|
||||||
self.primary_output_connected_to_layer(&node_id, network_path),
|
|
||||||
self.primary_input_connected_to_layer(&node_id, network_path),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(false, false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(position) = self.position(&node_id, network_path) else {
|
let Some(position) = self.position(&node_id, network_path) else {
|
||||||
log::error!("Could not get position for node: {node_id}");
|
log::error!("Could not get position for node: {node_id}");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
let position = FrontendXY { x: position.x, y: position.y };
|
||||||
let previewed = self.previewed_node(network_path) == Some(node_id);
|
let previewed = self.previewed_node(network_path) == Some(node_id);
|
||||||
|
|
||||||
let locked = self.is_locked(&node_id, network_path);
|
let locked = self.is_locked(&node_id, network_path);
|
||||||
@@ -70,19 +64,24 @@ impl NodeNetworkInterface {
|
|||||||
id: node_id,
|
id: node_id,
|
||||||
is_layer: self.node_metadata(&node_id, network_path).is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
is_layer: self.node_metadata(&node_id, network_path).is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
||||||
can_be_layer: self.is_eligible_to_be_layer(&node_id, network_path),
|
can_be_layer: self.is_eligible_to_be_layer(&node_id, network_path),
|
||||||
|
selected: selected_nodes.0.contains(&node_id),
|
||||||
reference: self.reference(&node_id, network_path).cloned().unwrap_or_default(),
|
reference: self.reference(&node_id, network_path).cloned().unwrap_or_default(),
|
||||||
display_name: self.display_name(&node_id, network_path),
|
display_name: self.display_name(&node_id, network_path),
|
||||||
|
previewed,
|
||||||
|
visible,
|
||||||
|
errors,
|
||||||
|
|
||||||
primary_input,
|
primary_input,
|
||||||
exposed_inputs,
|
exposed_inputs,
|
||||||
primary_output,
|
primary_output,
|
||||||
exposed_outputs,
|
exposed_outputs,
|
||||||
primary_output_connected_to_layer,
|
|
||||||
primary_input_connected_to_layer,
|
|
||||||
position,
|
position,
|
||||||
previewed,
|
|
||||||
visible,
|
|
||||||
locked,
|
locked,
|
||||||
errors,
|
chain_width: self.chain_width(&node_id, network_path),
|
||||||
|
layer_has_left_border_gap: self.layer_has_left_border_gap(&node_id, network_path),
|
||||||
|
primary_input_connected_to_layer: self.primary_output_connected_to_layer(&node_id, network_path),
|
||||||
|
primary_output_connected_to_layer: self.primary_input_connected_to_layer(&node_id, network_path),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,9 +258,12 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
/// Checks if a layer should display a gap in its left border
|
/// Checks if a layer should display a gap in its left border
|
||||||
pub fn layer_has_left_border_gap(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn layer_has_left_border_gap(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow)
|
self.upstream_flow_back_from_nodes(vec![*node_id], network_path, FlowType::HorizontalFlow).skip(1).any(|node_id| {
|
||||||
.skip(1)
|
!self.is_chain(&node_id, network_path)
|
||||||
.any(|node_id| !self.is_chain(&node_id, network_path))
|
|| self
|
||||||
|
.upstream_output_connector(&InputConnector::node(node_id, 0), network_path)
|
||||||
|
.is_some_and(|output_connector| matches!(output_connector, OutputConnector::Import(_)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the node which should have a dashed border drawn around it
|
/// Returns the node which should have a dashed border drawn around it
|
||||||
|
|||||||
@@ -116,12 +116,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function toggleLayerDisplay(displayAsLayer: boolean, toggleId: bigint) {
|
function toggleLayerDisplay(displayAsLayer: boolean, toggleId: bigint) {
|
||||||
let node = $nodeGraph.nodes.get(toggleId);
|
editor.handle.setToNodeOrLayer(toggleId, displayAsLayer);
|
||||||
if (node) editor.handle.setToNodeOrLayer(node.id, displayAsLayer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function canBeToggledBetweenNodeAndLayer(toggleDisplayAsLayerNodeId: bigint) {
|
function canBeToggledBetweenNodeAndLayer(toggleDisplayAsLayerNodeId: bigint) {
|
||||||
return $nodeGraph.nodes.get(toggleDisplayAsLayerNodeId)?.canBeLayer || false;
|
return $nodeGraph.nodesToRender.get(toggleDisplayAsLayerNodeId)?.canBeLayer || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNode(nodeType: string) {
|
function createNode(nodeType: string) {
|
||||||
@@ -148,7 +147,7 @@
|
|||||||
return borderMask(boxes, nodeWidth, nodeHeight);
|
return borderMask(boxes, nodeWidth, nodeHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
function layerBorderMask(nodeWidthFromThumbnail: number, nodeChainAreaLeftExtension: number, hasLeftInputWire: boolean): string {
|
function layerBorderMask(nodeWidthFromThumbnail: number, nodeChainAreaLeftExtension: number, layerHasLeftBorderGap: boolean): string {
|
||||||
const NODE_HEIGHT = 2 * 24;
|
const NODE_HEIGHT = 2 * 24;
|
||||||
const THUMBNAIL_WIDTH = 72 + 8 * 2;
|
const THUMBNAIL_WIDTH = 72 + 8 * 2;
|
||||||
const FUDGE_HEIGHT_BEYOND_LAYER_HEIGHT = 2;
|
const FUDGE_HEIGHT_BEYOND_LAYER_HEIGHT = 2;
|
||||||
@@ -158,7 +157,7 @@
|
|||||||
const boxes: { x: number; y: number; width: number; height: number }[] = [];
|
const boxes: { x: number; y: number; width: number; height: number }[] = [];
|
||||||
|
|
||||||
// Left input
|
// Left input
|
||||||
if (hasLeftInputWire && nodeChainAreaLeftExtension > 0) {
|
if (layerHasLeftBorderGap && nodeChainAreaLeftExtension > 0) {
|
||||||
boxes.push({ x: -8, y: 16, width: 16, height: 16 });
|
boxes.push({ x: -8, y: 16, width: 16, height: 16 });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,8 +317,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector"
|
class="connector"
|
||||||
data-connector="output"
|
|
||||||
data-datatype={frontendOutput.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${frontendOutput.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${frontendOutput.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${frontendOutput.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${frontendOutput.dataType.toLowerCase()}-dim)`}
|
||||||
style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24}
|
style:--offset-left={($nodeGraph.updateImportsExports.importPosition.x - 8) / 24}
|
||||||
@@ -390,8 +387,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector"
|
class="connector"
|
||||||
data-connector="input"
|
|
||||||
data-datatype={frontendInput.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${frontendInput.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${frontendInput.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${frontendInput.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${frontendInput.dataType.toLowerCase()}-dim)`}
|
||||||
style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 8) / 24}
|
style:--offset-left={($nodeGraph.updateImportsExports.exportPosition.x - 8) / 24}
|
||||||
@@ -492,54 +487,47 @@
|
|||||||
<!-- Layers and nodes -->
|
<!-- Layers and nodes -->
|
||||||
<div class="layers-and-nodes" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
<div class="layers-and-nodes" style:transform-origin="0 0" style:transform={`translate(${$nodeGraph.transform.x}px, ${$nodeGraph.transform.y}px) scale(${$nodeGraph.transform.scale})`}>
|
||||||
<!-- Layers -->
|
<!-- Layers -->
|
||||||
{#each Array.from($nodeGraph.nodes)
|
{#each Array.from($nodeGraph.nodesToRender).filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) as [nodeId, layer]}
|
||||||
.filter(([nodeId, node]) => node.isLayer && $nodeGraph.visibleNodes.has(nodeId))
|
|
||||||
.map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)}
|
|
||||||
{@const clipPathId = String(Math.random()).substring(2)}
|
{@const clipPathId = String(Math.random()).substring(2)}
|
||||||
{@const stackDataInput = node.exposedInputs[0]}
|
{@const layerAreaWidth = $nodeGraph.layerWidths.get(layer.id) || 8}
|
||||||
{@const layerAreaWidth = $nodeGraph.layerWidths.get(node.id) || 8}
|
{@const layerChainWidth = layer.chainWidth !== 0 ? layer.chainWidth + 0.5 : 0}
|
||||||
{@const layerChainWidth = $nodeGraph.chainWidths.get(node.id) || 0}
|
{@const description = (layer.reference && $nodeGraph.nodeDescriptions.get(layer.reference)) || undefined}
|
||||||
{@const hasLeftInputWire = $nodeGraph.hasLeftInputWire.get(node.id) || false}
|
|
||||||
{@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
|
|
||||||
<div
|
<div
|
||||||
class="layer"
|
class="layer"
|
||||||
class:selected={$nodeGraph.selected.includes(node.id)}
|
class:selected={layer.selected}
|
||||||
class:in-selected-network={$nodeGraph.inSelectedNetwork}
|
class:in-selected-network={$nodeGraph.inSelectedNetwork}
|
||||||
class:previewed={node.previewed}
|
class:previewed={layer.previewed}
|
||||||
class:disabled={!node.visible}
|
class:disabled={!layer.visible}
|
||||||
style:--offset-left={node.position?.x || 0}
|
style:--offset-left={layer.position.x}
|
||||||
style:--offset-top={node.position?.y || 0}
|
style:--offset-top={layer.position.y}
|
||||||
style:--clip-path-id={`url(#${clipPathId})`}
|
style:--clip-path-id={`url(#${clipPathId})`}
|
||||||
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
|
style:--data-color={`var(--color-data-${(layer.primaryOutput?.dataType || "General").toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${(layer.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||||
style:--layer-area-width={layerAreaWidth}
|
style:--layer-area-width={layerAreaWidth}
|
||||||
style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0}
|
style:--node-chain-area-left-extension={layerChainWidth}
|
||||||
title={`${node.displayName}\n\n${description || ""}`.trim() + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${node.id}` : "")}
|
title={`${layer.displayName}\n\n${description || ""}`.trim() + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${nodeId}` : "")}
|
||||||
data-node={node.id}
|
|
||||||
>
|
>
|
||||||
{#if node.errors}
|
{#if layer.errors}
|
||||||
<span class="node-error faded" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
<span class="node-error faded" transition:fade={FADE_TRANSITION} title="" data-node-error>{layer.errors}</span>
|
||||||
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
<span class="node-error hover" transition:fade={FADE_TRANSITION} title="" data-node-error>{layer.errors}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
{#if $nodeGraph.thumbnails.has(node.id)}
|
{#if $nodeGraph.thumbnails.has(nodeId)}
|
||||||
{@html $nodeGraph.thumbnails.get(node.id)}
|
{@html $nodeGraph.thumbnails.get(nodeId)}
|
||||||
{/if}
|
{/if}
|
||||||
<!-- Layer stacking top output -->
|
<!-- Layer stacking top output -->
|
||||||
{#if node.primaryOutput}
|
{#if layer.primaryOutput}
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 12"
|
viewBox="0 0 8 12"
|
||||||
class="connector top"
|
class="connector top"
|
||||||
data-connector="output"
|
style:--data-color={`var(--color-data-${layer.primaryOutput.dataType.toLowerCase()})`}
|
||||||
data-datatype={node.primaryOutput.dataType}
|
style:--data-color-dim={`var(--color-data-${layer.primaryOutput.dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`}
|
|
||||||
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`}
|
|
||||||
>
|
>
|
||||||
<title>{outputTooltip(node.primaryOutput)}</title>
|
<title>{outputTooltip(layer.primaryOutput)}</title>
|
||||||
{#if node.primaryOutput.connectedTo.length > 0}
|
{#if layer.primaryOutput.connectedTo.length > 0}
|
||||||
<path d="M0,6.953l2.521,-1.694a2.649,2.649,0,0,1,2.959,0l2.52,1.694v5.047h-8z" fill="var(--data-color)" />
|
<path d="M0,6.953l2.521,-1.694a2.649,2.649,0,0,1,2.959,0l2.52,1.694v5.047h-8z" fill="var(--data-color)" />
|
||||||
{#if node.primaryOutputConnectedToLayer}
|
{#if layer.primaryOutputConnectedToLayer}
|
||||||
<path d="M0,-3.5h8v8l-2.521,-1.681a2.666,2.666,0,0,0,-2.959,0l-2.52,1.681z" fill="var(--data-color-dim)" />
|
<path d="M0,-3.5h8v8l-2.521,-1.681a2.666,2.666,0,0,0,-2.959,0l-2.52,1.681z" fill="var(--data-color-dim)" />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
@@ -552,17 +540,15 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 12"
|
viewBox="0 0 8 12"
|
||||||
class="connector bottom"
|
class="connector bottom"
|
||||||
data-connector="input"
|
style:--data-color={`var(--color-data-${(layer.primaryInput?.dataType || "General").toLowerCase()})`}
|
||||||
data-datatype={node.primaryInput?.dataType}
|
style:--data-color-dim={`var(--color-data-${(layer.primaryInput?.dataType || "General").toLowerCase()}-dim)`}
|
||||||
style:--data-color={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()})`}
|
|
||||||
style:--data-color-dim={`var(--color-data-${(node.primaryInput?.dataType || "General").toLowerCase()}-dim)`}
|
|
||||||
>
|
>
|
||||||
{#if node.primaryInput}
|
{#if layer.primaryInput}
|
||||||
<title>{inputTooltip(node.primaryInput)}</title>
|
<title>{inputTooltip(layer.primaryInput)}</title>
|
||||||
{/if}
|
{/if}
|
||||||
{#if node.primaryInput?.connectedTo !== "nothing"}
|
{#if layer.primaryInput?.connectedTo !== "nothing"}
|
||||||
<path d="M0,0H8V8L5.479,6.319a2.666,2.666,0,0,0-2.959,0L0,8Z" fill="var(--data-color)" />
|
<path d="M0,0H8V8L5.479,6.319a2.666,2.666,0,0,0-2.959,0L0,8Z" fill="var(--data-color)" />
|
||||||
{#if node.primaryInputConnectedToLayer}
|
{#if layer.primaryInputConnectedToLayer}
|
||||||
<path d="M0,10.95l2.52,-1.69c0.89,-0.6,2.06,-0.6,2.96,0l2.52,1.69v5.05h-8v-5.05z" fill="var(--data-color-dim)" />
|
<path d="M0,10.95l2.52,-1.69c0.89,-0.6,2.06,-0.6,2.96,0l2.52,1.69v5.05h-8v-5.05z" fill="var(--data-color-dim)" />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
@@ -571,19 +557,17 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<!-- Layer input connector (from left) -->
|
<!-- Layer input connector (from left) -->
|
||||||
{#if node.exposedInputs.length > 0}
|
{#if layer.exposedInputs.length > 0}
|
||||||
<div class="input connectors">
|
<div class="input connectors">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector"
|
class="connector"
|
||||||
data-connector="input"
|
style:--data-color={`var(--color-data-${layer.exposedInputs[0].dataType.toLowerCase()})`}
|
||||||
data-datatype={stackDataInput.dataType}
|
style:--data-color-dim={`var(--color-data-${layer.exposedInputs[0].dataType.toLowerCase()}-dim)`}
|
||||||
style:--data-color={`var(--color-data-${stackDataInput.dataType.toLowerCase()})`}
|
|
||||||
style:--data-color-dim={`var(--color-data-${stackDataInput.dataType.toLowerCase()}-dim)`}
|
|
||||||
>
|
>
|
||||||
<title>{inputTooltip(stackDataInput)}</title>
|
<title>{inputTooltip(layer.exposedInputs[0])}</title>
|
||||||
{#if stackDataInput.connectedTo !== undefined}
|
{#if layer.exposedInputs[0].connectedTo !== undefined}
|
||||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color)" />
|
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color)" />
|
||||||
{:else}
|
{:else}
|
||||||
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color-dim)" />
|
<path d="M0,6.306A1.474,1.474,0,0,0,2.356,7.724L7.028,5.248c1.3-.687,1.3-1.809,0-2.5L2.356.276A1.474,1.474,0,0,0,0,1.694Z" fill="var(--data-color-dim)" />
|
||||||
@@ -593,25 +577,25 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
|
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
|
||||||
<TextLabel>{node.displayName}</TextLabel>
|
<TextLabel>{layer.displayName}</TextLabel>
|
||||||
</div>
|
</div>
|
||||||
<div class="solo-drag-grip" title="Drag only this layer without pushing others outside the stack"></div>
|
<div class="solo-drag-grip" title="Drag only this layer without pushing others outside the stack"></div>
|
||||||
<IconButton
|
<IconButton
|
||||||
class="visibility"
|
class="visibility"
|
||||||
data-visibility-button
|
data-visibility-button
|
||||||
size={24}
|
size={24}
|
||||||
icon={node.visible ? "EyeVisible" : "EyeHidden"}
|
icon={layer.visible ? "EyeVisible" : "EyeHidden"}
|
||||||
action={() => {
|
action={() => {
|
||||||
/* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */
|
/* Button is purely visual, clicking is handled in NodeGraphMessage::PointerDown */
|
||||||
}}
|
}}
|
||||||
tooltip={node.visible ? "Visible" : "Hidden"}
|
tooltip={layer.visible ? "Visible" : "Hidden"}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<svg class="border-mask" width="0" height="0">
|
<svg class="border-mask" width="0" height="0">
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id={clipPathId}>
|
<clipPath id={clipPathId}>
|
||||||
<!-- Keep this equation in sync with the equivalent one in the CSS rule for `.layer { width: ... }` below -->
|
<!-- Keep this equation in sync with the equivalent one in the CSS rule for `.layer { width: ... }` below -->
|
||||||
<path clip-rule="evenodd" d={layerBorderMask(24 * layerAreaWidth - 12, layerChainWidth ? (0.5 + layerChainWidth) * 24 : 0, hasLeftInputWire)} />
|
<path clip-rule="evenodd" d={layerBorderMask(24 * layerAreaWidth - 12, layerChainWidth * 24, layer.layerHasLeftBorderGap)} />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -647,15 +631,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Nodes -->
|
<!-- Nodes -->
|
||||||
{#each Array.from($nodeGraph.nodes)
|
{#each Array.from($nodeGraph.nodesToRender).filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodes.has(nodeId)) as [nodeId, node]}
|
||||||
.filter(([nodeId, node]) => !node.isLayer && $nodeGraph.visibleNodes.has(nodeId))
|
|
||||||
.map(([_, node], nodeIndex) => ({ node, nodeIndex })) as { node, nodeIndex } (nodeIndex)}
|
|
||||||
{@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)}
|
{@const exposedInputsOutputs = zipWithUndefined(node.exposedInputs, node.exposedOutputs)}
|
||||||
{@const clipPathId = String(Math.random()).substring(2)}
|
{@const clipPathId = String(Math.random()).substring(2)}
|
||||||
{@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
|
{@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
|
||||||
<div
|
<div
|
||||||
class="node"
|
class="node"
|
||||||
class:selected={$nodeGraph.selected.includes(node.id)}
|
class:selected={node.selected}
|
||||||
class:previewed={node.previewed}
|
class:previewed={node.previewed}
|
||||||
class:disabled={!node.visible}
|
class:disabled={!node.visible}
|
||||||
style:--offset-left={node.position?.x || 0}
|
style:--offset-left={node.position?.x || 0}
|
||||||
@@ -663,8 +645,7 @@
|
|||||||
style:--clip-path-id={`url(#${clipPathId})`}
|
style:--clip-path-id={`url(#${clipPathId})`}
|
||||||
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
|
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||||
title={`${node.displayName}\n\n${description || ""}`.trim() + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${node.id}` : "")}
|
title={`${node.displayName}\n\n${description || ""}`.trim() + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${nodeId}` : "")}
|
||||||
data-node={node.id}
|
|
||||||
>
|
>
|
||||||
{#if node.errors}
|
{#if node.errors}
|
||||||
<span class="node-error faded" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
<span class="node-error faded" transition:fade={FADE_TRANSITION} title="" data-node-error>{node.errors}</span>
|
||||||
@@ -695,8 +676,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector primary-connector"
|
class="connector primary-connector"
|
||||||
data-connector="input"
|
|
||||||
data-datatype={node.primaryInput?.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${node.primaryInput.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${node.primaryInput.dataType.toLowerCase()}-dim)`}
|
||||||
>
|
>
|
||||||
@@ -714,8 +693,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector"
|
class="connector"
|
||||||
data-connector="input"
|
|
||||||
data-datatype={secondary.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
||||||
>
|
>
|
||||||
@@ -736,8 +713,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector primary-connector"
|
class="connector primary-connector"
|
||||||
data-connector="output"
|
|
||||||
data-datatype={node.primaryOutput.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${node.primaryOutput.dataType.toLowerCase()}-dim)`}
|
||||||
>
|
>
|
||||||
@@ -754,8 +729,6 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 8 8"
|
viewBox="0 0 8 8"
|
||||||
class="connector"
|
class="connector"
|
||||||
data-connector="output"
|
|
||||||
data-datatype={secondary.dataType}
|
|
||||||
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
style:--data-color={`var(--color-data-${secondary.dataType.toLowerCase()})`}
|
||||||
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
style:--data-color-dim={`var(--color-data-${secondary.dataType.toLowerCase()}-dim)`}
|
||||||
>
|
>
|
||||||
@@ -784,7 +757,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Box selection widget -->
|
<!-- Box selection widget -->
|
||||||
<!-- TODO: Make its initial corner stay put (in graph space) when panning around -->
|
|
||||||
{#if $nodeGraph.box}
|
{#if $nodeGraph.box}
|
||||||
<div
|
<div
|
||||||
class="box-selection"
|
class="box-selection"
|
||||||
|
|||||||
+12
-22
@@ -55,19 +55,13 @@ export class UpdateImportsExports extends JsMessage {
|
|||||||
|
|
||||||
readonly exports!: (FrontendGraphInput | undefined)[];
|
readonly exports!: (FrontendGraphInput | undefined)[];
|
||||||
|
|
||||||
@TupleToVec2
|
|
||||||
readonly importPosition!: XY;
|
readonly importPosition!: XY;
|
||||||
|
|
||||||
@TupleToVec2
|
|
||||||
readonly exportPosition!: XY;
|
readonly exportPosition!: XY;
|
||||||
|
|
||||||
readonly addImportExport!: boolean;
|
readonly addImportExport!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateInSelectedNetwork extends JsMessage {
|
|
||||||
readonly inSelectedNetwork!: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateImportReorderIndex extends JsMessage {
|
export class UpdateImportReorderIndex extends JsMessage {
|
||||||
readonly importIndex!: number | undefined;
|
readonly importIndex!: number | undefined;
|
||||||
}
|
}
|
||||||
@@ -77,21 +71,18 @@ export class UpdateExportReorderIndex extends JsMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const LayerWidths = Transform(({ obj }) => obj.layerWidths);
|
const LayerWidths = Transform(({ obj }) => obj.layerWidths);
|
||||||
const ChainWidths = Transform(({ obj }) => obj.chainWidths);
|
|
||||||
const HasLeftInputWire = Transform(({ obj }) => obj.hasLeftInputWire);
|
|
||||||
|
|
||||||
export class UpdateLayerWidths extends JsMessage {
|
export class UpdateLayerWidths extends JsMessage {
|
||||||
@LayerWidths
|
@LayerWidths
|
||||||
readonly layerWidths!: Map<bigint, number>;
|
readonly layerWidths!: Map<bigint, number>;
|
||||||
@ChainWidths
|
|
||||||
readonly chainWidths!: Map<bigint, number>;
|
|
||||||
@HasLeftInputWire
|
|
||||||
readonly hasLeftInputWire!: Map<bigint, boolean>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateNodeGraphNodes extends JsMessage {
|
export class UpdateNodeGraphNodes extends JsMessage {
|
||||||
@Type(() => FrontendNode)
|
readonly nodesToRender!: FrontendNode[];
|
||||||
readonly nodes!: FrontendNode[];
|
|
||||||
|
readonly inSelectedNetwork!: boolean;
|
||||||
|
|
||||||
|
readonly previewedNode!: bigint | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateVisibleNodes extends JsMessage {
|
export class UpdateVisibleNodes extends JsMessage {
|
||||||
@@ -123,11 +114,6 @@ export class UpdateNodeThumbnail extends JsMessage {
|
|||||||
readonly value!: string;
|
readonly value!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateNodeGraphSelection extends JsMessage {
|
|
||||||
@Type(() => BigInt)
|
|
||||||
readonly selected!: bigint[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateOpenDocumentsList extends JsMessage {
|
export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
@Type(() => OpenDocument)
|
@Type(() => OpenDocument)
|
||||||
readonly openDocuments!: OpenDocument[];
|
readonly openDocuments!: OpenDocument[];
|
||||||
@@ -211,11 +197,13 @@ export class FrontendGraphOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FrontendNode {
|
export class FrontendNode {
|
||||||
|
readonly id!: bigint;
|
||||||
|
|
||||||
readonly isLayer!: boolean;
|
readonly isLayer!: boolean;
|
||||||
|
|
||||||
readonly canBeLayer!: boolean;
|
readonly canBeLayer!: boolean;
|
||||||
|
|
||||||
readonly id!: bigint;
|
readonly selected!: boolean;
|
||||||
|
|
||||||
readonly reference!: string | undefined;
|
readonly reference!: string | undefined;
|
||||||
|
|
||||||
@@ -229,6 +217,10 @@ export class FrontendNode {
|
|||||||
|
|
||||||
readonly exposedOutputs!: FrontendGraphOutput[];
|
readonly exposedOutputs!: FrontendGraphOutput[];
|
||||||
|
|
||||||
|
readonly chainWidth!: number;
|
||||||
|
|
||||||
|
readonly layerHasLeftBorderGap!: boolean;
|
||||||
|
|
||||||
readonly primaryInputConnectedToLayer!: boolean;
|
readonly primaryInputConnectedToLayer!: boolean;
|
||||||
|
|
||||||
readonly primaryOutputConnectedToLayer!: boolean;
|
readonly primaryOutputConnectedToLayer!: boolean;
|
||||||
@@ -1672,7 +1664,6 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateImportReorderIndex,
|
UpdateImportReorderIndex,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
UpdateInputHints,
|
UpdateInputHints,
|
||||||
UpdateInSelectedNetwork,
|
|
||||||
UpdateLayersPanelBottomBarLayout,
|
UpdateLayersPanelBottomBarLayout,
|
||||||
UpdateLayersPanelControlBarLeftLayout,
|
UpdateLayersPanelControlBarLeftLayout,
|
||||||
UpdateLayersPanelControlBarRightLayout,
|
UpdateLayersPanelControlBarRightLayout,
|
||||||
@@ -1682,7 +1673,6 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||||||
UpdateMouseCursor,
|
UpdateMouseCursor,
|
||||||
UpdateNodeGraphControlBarLayout,
|
UpdateNodeGraphControlBarLayout,
|
||||||
UpdateNodeGraphNodes,
|
UpdateNodeGraphNodes,
|
||||||
UpdateNodeGraphSelection,
|
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeGraphWires,
|
UpdateNodeGraphWires,
|
||||||
UpdateNodeThumbnail,
|
UpdateNodeThumbnail,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
UpdateBox,
|
UpdateBox,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
UpdateInSelectedNetwork,
|
|
||||||
UpdateImportReorderIndex,
|
UpdateImportReorderIndex,
|
||||||
UpdateExportReorderIndex,
|
UpdateExportReorderIndex,
|
||||||
UpdateImportsExports,
|
UpdateImportsExports,
|
||||||
@@ -21,7 +20,6 @@ import {
|
|||||||
UpdateNodeGraphNodes,
|
UpdateNodeGraphNodes,
|
||||||
UpdateVisibleNodes,
|
UpdateVisibleNodes,
|
||||||
UpdateNodeGraphWires,
|
UpdateNodeGraphWires,
|
||||||
UpdateNodeGraphSelection,
|
|
||||||
UpdateNodeGraphTransform,
|
UpdateNodeGraphTransform,
|
||||||
UpdateNodeThumbnail,
|
UpdateNodeThumbnail,
|
||||||
UpdateWirePathInProgress,
|
UpdateWirePathInProgress,
|
||||||
@@ -33,10 +31,9 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
clickTargets: undefined as FrontendClickTargets | undefined,
|
clickTargets: undefined as FrontendClickTargets | undefined,
|
||||||
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
contextMenuInformation: undefined as ContextMenuInformation | undefined,
|
||||||
layerWidths: new Map<bigint, number>(),
|
layerWidths: new Map<bigint, number>(),
|
||||||
chainWidths: new Map<bigint, number>(),
|
|
||||||
hasLeftInputWire: new Map<bigint, boolean>(),
|
|
||||||
updateImportsExports: undefined as UpdateImportsExports | undefined,
|
updateImportsExports: undefined as UpdateImportsExports | undefined,
|
||||||
nodes: new Map<bigint, FrontendNode>(),
|
nodesToRender: new Map<bigint, FrontendNode>(),
|
||||||
|
|
||||||
visibleNodes: new Set<bigint>(),
|
visibleNodes: new Set<bigint>(),
|
||||||
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
|
/// The index is the exposed input index. The exports have a first key value of u32::MAX.
|
||||||
wires: new Map<bigint, Map<number, WirePath>>(),
|
wires: new Map<bigint, Map<number, WirePath>>(),
|
||||||
@@ -44,9 +41,9 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
nodeDescriptions: new Map<string, string>(),
|
nodeDescriptions: new Map<string, string>(),
|
||||||
nodeTypes: [] as FrontendNodeType[],
|
nodeTypes: [] as FrontendNodeType[],
|
||||||
thumbnails: new Map<bigint, string>(),
|
thumbnails: new Map<bigint, string>(),
|
||||||
selected: [] as bigint[],
|
|
||||||
transform: { scale: 1, x: 0, y: 0 },
|
transform: { scale: 1, x: 0, y: 0 },
|
||||||
inSelectedNetwork: true,
|
inSelectedNetwork: true,
|
||||||
|
previewedNode: undefined as bigint | undefined,
|
||||||
reorderImportIndex: undefined as number | undefined,
|
reorderImportIndex: undefined as number | undefined,
|
||||||
reorderExportIndex: undefined as number | undefined,
|
reorderExportIndex: undefined as number | undefined,
|
||||||
});
|
});
|
||||||
@@ -95,26 +92,20 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateInSelectedNetwork, (updateInSelectedNetwork) => {
|
|
||||||
update((state) => {
|
|
||||||
state.inSelectedNetwork = updateInSelectedNetwork.inSelectedNetwork;
|
|
||||||
return state;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateLayerWidths, (updateLayerWidths) => {
|
editor.subscriptions.subscribeJsMessage(UpdateLayerWidths, (updateLayerWidths) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.layerWidths = updateLayerWidths.layerWidths;
|
state.layerWidths = updateLayerWidths.layerWidths;
|
||||||
state.chainWidths = updateLayerWidths.chainWidths;
|
|
||||||
state.hasLeftInputWire = updateLayerWidths.hasLeftInputWire;
|
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphNodes, (updateNodeGraphNodes) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.nodes.clear();
|
state.nodesToRender.clear();
|
||||||
updateNodeGraphNodes.nodes.forEach((node) => {
|
updateNodeGraphNodes.nodesToRender.forEach((node) => {
|
||||||
state.nodes.set(node.id, node);
|
state.nodesToRender.set(node.id, node);
|
||||||
});
|
});
|
||||||
|
state.inSelectedNetwork = updateNodeGraphNodes.inSelectedNetwork;
|
||||||
|
state.previewedNode = updateNodeGraphNodes.previewedNode;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -148,12 +139,6 @@ export function createNodeGraphState(editor: Editor) {
|
|||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphSelection, (updateNodeGraphSelection) => {
|
|
||||||
update((state) => {
|
|
||||||
state.selected = updateNodeGraphSelection.selected;
|
|
||||||
return state;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => {
|
editor.subscriptions.subscribeJsMessage(UpdateNodeGraphTransform, (updateNodeGraphTransform) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.transform = updateNodeGraphTransform.transform;
|
state.transform = updateNodeGraphTransform.transform;
|
||||||
|
|||||||
Reference in New Issue
Block a user