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 a5b29c8d9..53b225545 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 @@ -14,7 +14,7 @@ pub enum NodeGraphMessage { }, CreateNode { // Having the caller generate the id means that we don't have to return it. This can be a random u64. - node_id: NodeId, + node_id: Option, // I don't really know what this is for (perhaps a user identifiable name). node_type: String, }, 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 81a1aba2c..796f0763b 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 @@ -35,6 +35,8 @@ pub struct FrontendNode { pub id: graph_craft::document::NodeId, #[serde(rename = "displayName")] pub display_name: String, + #[serde(rename = "primaryInput")] + pub primary_input: Option, #[serde(rename = "exposedInputs")] pub exposed_inputs: Vec, pub outputs: Vec, @@ -122,10 +124,17 @@ impl NodeGraphMessageHandler { nodes.push(FrontendNode { id: *id, display_name: node.name.clone(), + primary_input: node + .inputs + .first() + .filter(|input| input.is_exposed()) + .and_then(|_| node_type.inputs.get(0)) + .map(|input_type| input_type.data_type), exposed_inputs: node .inputs .iter() .zip(node_type.inputs) + .skip(1) .filter(|(input, _)| input.is_exposed()) .map(|(_, input_type)| NodeGraphInput { data_type: input_type.data_type, @@ -180,6 +189,7 @@ impl MessageHandler { + let node_id = node_id.unwrap_or_else(crate::application::generate_uuid); let Some(network) = self.get_active_network_mut(document) else{ warn!("No network"); return; @@ -208,6 +218,8 @@ impl MessageHandler { let Some(network) = self.get_active_network_mut(document) else{ diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index d9ae7010d..f5ea3d14f 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -37,7 +37,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ properties: |_document_node, _node_id| { vec![LayoutGroup::Row { widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel { - value: format!("The identity node simply returns the input"), + value: "The identity node simply returns the input".to_string(), ..Default::default() }))], }] @@ -46,12 +46,16 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ DocumentNodeType { name: "Input", identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]), - inputs: &[], + inputs: &[DocumentInputType { + name: "In", + data_type: FrontendGraphDataType::Raster, + default: NodeInput::Network, + }], outputs: &[FrontendGraphDataType::Raster], properties: |_document_node, _node_id| { vec![LayoutGroup::Row { widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel { - value: format!("The input to the graph is the bitmap under the frame"), + value: "The input to the graph is the bitmap under the frame".to_string(), ..Default::default() }))], }] @@ -72,7 +76,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ properties: |_document_node, _node_id| { vec![LayoutGroup::Row { widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel { - value: format!("The output to the graph is rendered in the frame"), + value: "The output to the graph is rendered in the frame".to_string(), ..Default::default() }))], }] @@ -93,7 +97,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ properties: |_document_node, _node_id| { vec![LayoutGroup::Row { widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel { - value: format!("The output to the graph is rendered in the frame"), + value: "The output to the graph is rendered in the frame".to_string(), ..Default::default() }))], }] @@ -152,7 +156,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]), inputs: &[ DocumentInputType { - name: "Left", + name: "Input", data_type: FrontendGraphDataType::Number, default: NodeInput::Value { tagged_value: TaggedValue::F32(0.), @@ -160,7 +164,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [ }, }, DocumentInputType { - name: "Right", + name: "Addend", data_type: FrontendGraphDataType::Number, default: NodeInput::Value { tagged_value: TaggedValue::F32(0.), @@ -178,5 +182,9 @@ pub fn resolve_document_node_type(name: &str) -> Option<&DocumentNodeType> { } pub fn collect_node_types() -> Vec { - DOCUMENT_NODE_TYPES.iter().map(|node_type| FrontendNodeType { name: node_type.name.to_string() }).collect() + DOCUMENT_NODE_TYPES + .iter() + .filter(|node_type| !matches!(node_type.name, "Input" | "Output")) + .map(|node_type| FrontendNodeType { name: node_type.name.to_string() }) + .collect() } diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index 2532b663a..60caa8ef5 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -10,33 +10,46 @@ use graph_craft::document::{DocumentNode, NodeId, NodeInput}; use super::FrontendGraphDataType; pub fn hue_shift_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec { - vec![LayoutGroup::Row { - widgets: vec![ - WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton { - exposed: true, - data_type: FrontendGraphDataType::Number, - tooltip: "Expose input parameter in node graph".into(), - ..Default::default() - })), - WidgetHolder::new(Widget::Separator(Separator { - separator_type: SeparatorType::Unrelated, - direction: SeparatorDirection::Horizontal, - })), - WidgetHolder::new(Widget::TextLabel(TextLabel { - value: "Shift Degrees".into(), - ..Default::default() - })), + let index = 1; + let input: &NodeInput = document_node.inputs.get(index).unwrap(); + let exposed = input.is_exposed(); + + let mut widgets = vec![ + WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton { + exposed, + data_type: FrontendGraphDataType::Number, + tooltip: "Expose input parameter in node graph".into(), + on_update: WidgetCallback::new(move |_parameter| { + NodeGraphMessage::ExposeInput { + node_id, + input_index: index, + new_exposed: !exposed, + } + .into() + }), + ..Default::default() + })), + WidgetHolder::new(Widget::Separator(Separator { + separator_type: SeparatorType::Unrelated, + direction: SeparatorDirection::Horizontal, + })), + WidgetHolder::new(Widget::TextLabel(TextLabel { + value: "Shift Degrees".into(), + ..Default::default() + })), + ]; + if let NodeInput::Value { + tagged_value: TaggedValue::F32(x), + exposed: false, + } = document_node.inputs[index] + { + widgets.extend_from_slice(&[ WidgetHolder::new(Widget::Separator(Separator { separator_type: SeparatorType::Unrelated, direction: SeparatorDirection::Horizontal, })), WidgetHolder::new(Widget::NumberInput(NumberInput { - value: Some({ - let NodeInput::Value {tagged_value: TaggedValue::F32(x), ..} = document_node.inputs[1] else { - panic!("Hue rotate should be f32") - }; - x as f64 - }), + value: Some(x as f64), unit: "°".into(), mode: NumberInputMode::Range, range_min: Some(-180.), @@ -51,38 +64,54 @@ pub fn hue_shift_image_properties(document_node: &DocumentNode, node_id: NodeId) }), ..NumberInput::default() })), - ], - }] + ]) + } + + vec![LayoutGroup::Row { widgets }] } pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec { - vec![LayoutGroup::Row { - widgets: vec![ - WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton { - exposed: true, - data_type: FrontendGraphDataType::Number, - tooltip: "Expose input parameter in node graph".into(), - ..Default::default() - })), - WidgetHolder::new(Widget::Separator(Separator { - separator_type: SeparatorType::Unrelated, - direction: SeparatorDirection::Horizontal, - })), - WidgetHolder::new(Widget::TextLabel(TextLabel { - value: "Brighten Amount".into(), - ..Default::default() - })), + let index = 1; + let input: &NodeInput = document_node.inputs.get(index).unwrap(); + let exposed = input.is_exposed(); + + let mut widgets = vec![ + WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton { + exposed, + data_type: FrontendGraphDataType::Number, + tooltip: "Expose input parameter in node graph".into(), + on_update: WidgetCallback::new(move |_parameter| { + NodeGraphMessage::ExposeInput { + node_id, + input_index: index, + new_exposed: !exposed, + } + .into() + }), + ..Default::default() + })), + WidgetHolder::new(Widget::Separator(Separator { + separator_type: SeparatorType::Unrelated, + direction: SeparatorDirection::Horizontal, + })), + WidgetHolder::new(Widget::TextLabel(TextLabel { + value: "Brighten Amount".into(), + ..Default::default() + })), + ]; + + if let NodeInput::Value { + tagged_value: TaggedValue::F32(x), + exposed: false, + } = document_node.inputs[index] + { + widgets.extend_from_slice(&[ WidgetHolder::new(Widget::Separator(Separator { separator_type: SeparatorType::Unrelated, direction: SeparatorDirection::Horizontal, })), WidgetHolder::new(Widget::NumberInput(NumberInput { - value: Some({ - let NodeInput::Value {tagged_value: TaggedValue::F32(x), ..} = document_node.inputs[1] else { - panic!("Brighten amount should be f32") - }; - x as f64 - }), + value: Some(x as f64), mode: NumberInputMode::Range, range_min: Some(-255.), range_max: Some(255.), @@ -96,17 +125,29 @@ pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId) }), ..NumberInput::default() })), - ], - }] + ]) + } + + vec![LayoutGroup::Row { widgets }] } pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec { - let operand = |name: &str, index| LayoutGroup::Row { - widgets: vec![ + let operand = |name: &str, index| { + let input: &NodeInput = document_node.inputs.get(index).unwrap(); + let exposed = input.is_exposed(); + let mut widgets = vec![ WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton { - exposed: true, + exposed, data_type: FrontendGraphDataType::Number, tooltip: "Expose input parameter in node graph".into(), + on_update: WidgetCallback::new(move |_parameter| { + NodeGraphMessage::ExposeInput { + node_id, + input_index: index, + new_exposed: !exposed, + } + .into() + }), ..Default::default() })), WidgetHolder::new(Widget::Separator(Separator { @@ -117,32 +158,37 @@ pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec Vec { diff --git a/frontend/src/components/panels/NodeGraph.vue b/frontend/src/components/panels/NodeGraph.vue index fb063eaa7..228ca2fa2 100644 --- a/frontend/src/components/panels/NodeGraph.vue +++ b/frontend/src/components/panels/NodeGraph.vue @@ -45,11 +45,11 @@
@@ -66,8 +66,8 @@ {{ node.displayName }}
-
-
+
+
{ - const connectorIndex = 0; + const connectorIndex = Number(link.linkEndInputIndex); const nodePrimaryOutput = (containerBounds.querySelector(`[data-node="${String(link.linkStart)}"] [data-port="output"]`) || undefined) as HTMLDivElement | undefined; @@ -511,8 +511,8 @@ export default defineComponent({ this.linkInProgressToConnector = new DOMRect(e.x, e.y); } } else if (this.draggingNodes) { - const deltaX = Math.round((e.x - this.draggingNodes.startX) / this.transform.scale / this.gridSpacing); - const deltaY = Math.round((e.y - this.draggingNodes.startY) / this.transform.scale / this.gridSpacing); + const deltaX = Math.round((e.x - this.draggingNodes.startX) / this.transform.scale / GRID_SIZE); + const deltaY = Math.round((e.y - this.draggingNodes.startY) / this.transform.scale / GRID_SIZE); if (this.draggingNodes.roundX !== deltaX || this.draggingNodes.roundY !== deltaY) { this.draggingNodes.roundX = deltaX; this.draggingNodes.roundY = deltaY; diff --git a/frontend/src/wasm-communication/messages.ts b/frontend/src/wasm-communication/messages.ts index a9a5ca082..c67bc4c51 100644 --- a/frontend/src/wasm-communication/messages.ts +++ b/frontend/src/wasm-communication/messages.ts @@ -82,6 +82,8 @@ export class FrontendNode { readonly displayName!: string; + readonly primaryInput!: FrontendGraphDataType | undefined; + readonly exposedInputs!: NodeGraphInput[]; readonly outputs!: FrontendGraphDataType[]; diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index 33ca925ac..1aafa9282 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -553,18 +553,7 @@ impl JsEditorHandle { /// Creates a new document node in the node graph #[wasm_bindgen(js_name = createNode)] pub fn create_node(&self, node_type: String) { - fn generate_node_id() -> u64 { - static mut NODE_ID: u64 = 10; - unsafe { - NODE_ID += 1; - NODE_ID - } - } - - let message = NodeGraphMessage::CreateNode { - node_id: generate_node_id(), - node_type, - }; + let message = NodeGraphMessage::CreateNode { node_id: None, node_type }; self.dispatch(message); } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 96ef31ac2..3386d64d1 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -108,10 +108,10 @@ impl NodeInput { } } pub fn is_exposed(&self) -> bool { - if let NodeInput::Value { exposed, .. } = self { - *exposed - } else { - true + match self { + NodeInput::Node(_) => true, + NodeInput::Value { exposed, .. } => *exposed, + NodeInput::Network => false, } } }