Skip to content

Commit 3dd9e88

Browse files
0HyperCubeKeavon
authored andcommitted
Add support for exposing node parameter inputs (#866)
* Add exposing inputs to graph * Use uuids and better node positioning * Fix accidentally refering to the wrong grid spacing * Secondary input without primary input * Cleanup document node types * Rename to input and addend
1 parent 0a78ebd commit 3dd9e88

File tree

8 files changed

+174
-109
lines changed

8 files changed

+174
-109
lines changed

editor/src/messages/portfolio/document/node_graph/node_graph_message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub enum NodeGraphMessage {
1414
},
1515
CreateNode {
1616
// Having the caller generate the id means that we don't have to return it. This can be a random u64.
17-
node_id: NodeId,
17+
node_id: Option<NodeId>,
1818
// I don't really know what this is for (perhaps a user identifiable name).
1919
node_type: String,
2020
},

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub struct FrontendNode {
3535
pub id: graph_craft::document::NodeId,
3636
#[serde(rename = "displayName")]
3737
pub display_name: String,
38+
#[serde(rename = "primaryInput")]
39+
pub primary_input: Option<FrontendGraphDataType>,
3840
#[serde(rename = "exposedInputs")]
3941
pub exposed_inputs: Vec<NodeGraphInput>,
4042
pub outputs: Vec<FrontendGraphDataType>,
@@ -122,10 +124,17 @@ impl NodeGraphMessageHandler {
122124
nodes.push(FrontendNode {
123125
id: *id,
124126
display_name: node.name.clone(),
127+
primary_input: node
128+
.inputs
129+
.first()
130+
.filter(|input| input.is_exposed())
131+
.and_then(|_| node_type.inputs.get(0))
132+
.map(|input_type| input_type.data_type),
125133
exposed_inputs: node
126134
.inputs
127135
.iter()
128136
.zip(node_type.inputs)
137+
.skip(1)
129138
.filter(|(input, _)| input.is_exposed())
130139
.map(|(_, input_type)| NodeGraphInput {
131140
data_type: input_type.data_type,
@@ -180,6 +189,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
180189
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
181190
}
182191
NodeGraphMessage::CreateNode { node_id, node_type } => {
192+
let node_id = node_id.unwrap_or_else(crate::application::generate_uuid);
183193
let Some(network) = self.get_active_network_mut(document) else{
184194
warn!("No network");
185195
return;
@@ -208,6 +218,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
208218
.into_iter()
209219
.collect(),
210220
};
221+
let far_right_node = network.nodes.iter().map(|node| node.1.metadata.position).max_by_key(|pos| pos.0).unwrap_or_default();
222+
211223
network.nodes.insert(
212224
node_id,
213225
DocumentNode {
@@ -217,7 +229,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
217229
implementation: DocumentNodeImplementation::Network(inner_network),
218230
metadata: graph_craft::document::DocumentNodeMetadata {
219231
// TODO: Better position default
220-
position: (node_id as i32 * 7 - 41, node_id as i32 * 2 - 10),
232+
position: (far_right_node.0 + 7, far_right_node.1 + 2),
221233
},
222234
},
223235
);
@@ -243,8 +255,16 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
243255

244256
if let NodeInput::Value { exposed, .. } = &mut node.inputs[input_index] {
245257
*exposed = new_exposed;
258+
} else if let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) {
259+
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
260+
node.inputs[input_index] = NodeInput::Value {
261+
tagged_value: tagged_value.clone(),
262+
exposed: new_exposed,
263+
};
264+
}
246265
}
247266
Self::send_graph(network, responses);
267+
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
248268
}
249269
NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y } => {
250270
let Some(network) = self.get_active_network_mut(document) else{

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
3737
properties: |_document_node, _node_id| {
3838
vec![LayoutGroup::Row {
3939
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
40-
value: format!("The identity node simply returns the input"),
40+
value: "The identity node simply returns the input".to_string(),
4141
..Default::default()
4242
}))],
4343
}]
@@ -46,12 +46,16 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
4646
DocumentNodeType {
4747
name: "Input",
4848
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
49-
inputs: &[],
49+
inputs: &[DocumentInputType {
50+
name: "In",
51+
data_type: FrontendGraphDataType::Raster,
52+
default: NodeInput::Network,
53+
}],
5054
outputs: &[FrontendGraphDataType::Raster],
5155
properties: |_document_node, _node_id| {
5256
vec![LayoutGroup::Row {
5357
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
54-
value: format!("The input to the graph is the bitmap under the frame"),
58+
value: "The input to the graph is the bitmap under the frame".to_string(),
5559
..Default::default()
5660
}))],
5761
}]
@@ -72,7 +76,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
7276
properties: |_document_node, _node_id| {
7377
vec![LayoutGroup::Row {
7478
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
75-
value: format!("The output to the graph is rendered in the frame"),
79+
value: "The output to the graph is rendered in the frame".to_string(),
7680
..Default::default()
7781
}))],
7882
}]
@@ -93,7 +97,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
9397
properties: |_document_node, _node_id| {
9498
vec![LayoutGroup::Row {
9599
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
96-
value: format!("The output to the graph is rendered in the frame"),
100+
value: "The output to the graph is rendered in the frame".to_string(),
97101
..Default::default()
98102
}))],
99103
}]
@@ -152,15 +156,15 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
152156
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
153157
inputs: &[
154158
DocumentInputType {
155-
name: "Left",
159+
name: "Input",
156160
data_type: FrontendGraphDataType::Number,
157161
default: NodeInput::Value {
158162
tagged_value: TaggedValue::F32(0.),
159163
exposed: true,
160164
},
161165
},
162166
DocumentInputType {
163-
name: "Right",
167+
name: "Addend",
164168
data_type: FrontendGraphDataType::Number,
165169
default: NodeInput::Value {
166170
tagged_value: TaggedValue::F32(0.),
@@ -178,5 +182,9 @@ pub fn resolve_document_node_type(name: &str) -> Option<&DocumentNodeType> {
178182
}
179183

180184
pub fn collect_node_types() -> Vec<FrontendNodeType> {
181-
DOCUMENT_NODE_TYPES.iter().map(|node_type| FrontendNodeType { name: node_type.name.to_string() }).collect()
185+
DOCUMENT_NODE_TYPES
186+
.iter()
187+
.filter(|node_type| !matches!(node_type.name, "Input" | "Output"))
188+
.map(|node_type| FrontendNodeType { name: node_type.name.to_string() })
189+
.collect()
182190
}

editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs

Lines changed: 121 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,46 @@ use graph_craft::document::{DocumentNode, NodeId, NodeInput};
1010
use super::FrontendGraphDataType;
1111

1212
pub fn hue_shift_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
13-
vec![LayoutGroup::Row {
14-
widgets: vec![
15-
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
16-
exposed: true,
17-
data_type: FrontendGraphDataType::Number,
18-
tooltip: "Expose input parameter in node graph".into(),
19-
..Default::default()
20-
})),
21-
WidgetHolder::new(Widget::Separator(Separator {
22-
separator_type: SeparatorType::Unrelated,
23-
direction: SeparatorDirection::Horizontal,
24-
})),
25-
WidgetHolder::new(Widget::TextLabel(TextLabel {
26-
value: "Shift Degrees".into(),
27-
..Default::default()
28-
})),
13+
let index = 1;
14+
let input: &NodeInput = document_node.inputs.get(index).unwrap();
15+
let exposed = input.is_exposed();
16+
17+
let mut widgets = vec![
18+
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
19+
exposed,
20+
data_type: FrontendGraphDataType::Number,
21+
tooltip: "Expose input parameter in node graph".into(),
22+
on_update: WidgetCallback::new(move |_parameter| {
23+
NodeGraphMessage::ExposeInput {
24+
node_id,
25+
input_index: index,
26+
new_exposed: !exposed,
27+
}
28+
.into()
29+
}),
30+
..Default::default()
31+
})),
32+
WidgetHolder::new(Widget::Separator(Separator {
33+
separator_type: SeparatorType::Unrelated,
34+
direction: SeparatorDirection::Horizontal,
35+
})),
36+
WidgetHolder::new(Widget::TextLabel(TextLabel {
37+
value: "Shift Degrees".into(),
38+
..Default::default()
39+
})),
40+
];
41+
if let NodeInput::Value {
42+
tagged_value: TaggedValue::F32(x),
43+
exposed: false,
44+
} = document_node.inputs[index]
45+
{
46+
widgets.extend_from_slice(&[
2947
WidgetHolder::new(Widget::Separator(Separator {
3048
separator_type: SeparatorType::Unrelated,
3149
direction: SeparatorDirection::Horizontal,
3250
})),
3351
WidgetHolder::new(Widget::NumberInput(NumberInput {
34-
value: Some({
35-
let NodeInput::Value {tagged_value: TaggedValue::F32(x), ..} = document_node.inputs[1] else {
36-
panic!("Hue rotate should be f32")
37-
};
38-
x as f64
39-
}),
52+
value: Some(x as f64),
4053
unit: "°".into(),
4154
mode: NumberInputMode::Range,
4255
range_min: Some(-180.),
@@ -51,38 +64,54 @@ pub fn hue_shift_image_properties(document_node: &DocumentNode, node_id: NodeId)
5164
}),
5265
..NumberInput::default()
5366
})),
54-
],
55-
}]
67+
])
68+
}
69+
70+
vec![LayoutGroup::Row { widgets }]
5671
}
5772

5873
pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
59-
vec![LayoutGroup::Row {
60-
widgets: vec![
61-
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
62-
exposed: true,
63-
data_type: FrontendGraphDataType::Number,
64-
tooltip: "Expose input parameter in node graph".into(),
65-
..Default::default()
66-
})),
67-
WidgetHolder::new(Widget::Separator(Separator {
68-
separator_type: SeparatorType::Unrelated,
69-
direction: SeparatorDirection::Horizontal,
70-
})),
71-
WidgetHolder::new(Widget::TextLabel(TextLabel {
72-
value: "Brighten Amount".into(),
73-
..Default::default()
74-
})),
74+
let index = 1;
75+
let input: &NodeInput = document_node.inputs.get(index).unwrap();
76+
let exposed = input.is_exposed();
77+
78+
let mut widgets = vec![
79+
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
80+
exposed,
81+
data_type: FrontendGraphDataType::Number,
82+
tooltip: "Expose input parameter in node graph".into(),
83+
on_update: WidgetCallback::new(move |_parameter| {
84+
NodeGraphMessage::ExposeInput {
85+
node_id,
86+
input_index: index,
87+
new_exposed: !exposed,
88+
}
89+
.into()
90+
}),
91+
..Default::default()
92+
})),
93+
WidgetHolder::new(Widget::Separator(Separator {
94+
separator_type: SeparatorType::Unrelated,
95+
direction: SeparatorDirection::Horizontal,
96+
})),
97+
WidgetHolder::new(Widget::TextLabel(TextLabel {
98+
value: "Brighten Amount".into(),
99+
..Default::default()
100+
})),
101+
];
102+
103+
if let NodeInput::Value {
104+
tagged_value: TaggedValue::F32(x),
105+
exposed: false,
106+
} = document_node.inputs[index]
107+
{
108+
widgets.extend_from_slice(&[
75109
WidgetHolder::new(Widget::Separator(Separator {
76110
separator_type: SeparatorType::Unrelated,
77111
direction: SeparatorDirection::Horizontal,
78112
})),
79113
WidgetHolder::new(Widget::NumberInput(NumberInput {
80-
value: Some({
81-
let NodeInput::Value {tagged_value: TaggedValue::F32(x), ..} = document_node.inputs[1] else {
82-
panic!("Brighten amount should be f32")
83-
};
84-
x as f64
85-
}),
114+
value: Some(x as f64),
86115
mode: NumberInputMode::Range,
87116
range_min: Some(-255.),
88117
range_max: Some(255.),
@@ -96,17 +125,29 @@ pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId)
96125
}),
97126
..NumberInput::default()
98127
})),
99-
],
100-
}]
128+
])
129+
}
130+
131+
vec![LayoutGroup::Row { widgets }]
101132
}
102133

103134
pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
104-
let operand = |name: &str, index| LayoutGroup::Row {
105-
widgets: vec![
135+
let operand = |name: &str, index| {
136+
let input: &NodeInput = document_node.inputs.get(index).unwrap();
137+
let exposed = input.is_exposed();
138+
let mut widgets = vec![
106139
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
107-
exposed: true,
140+
exposed,
108141
data_type: FrontendGraphDataType::Number,
109142
tooltip: "Expose input parameter in node graph".into(),
143+
on_update: WidgetCallback::new(move |_parameter| {
144+
NodeGraphMessage::ExposeInput {
145+
node_id,
146+
input_index: index,
147+
new_exposed: !exposed,
148+
}
149+
.into()
150+
}),
110151
..Default::default()
111152
})),
112153
WidgetHolder::new(Widget::Separator(Separator {
@@ -117,32 +158,37 @@ pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<Layo
117158
value: name.into(),
118159
..Default::default()
119160
})),
120-
WidgetHolder::new(Widget::Separator(Separator {
121-
separator_type: SeparatorType::Unrelated,
122-
direction: SeparatorDirection::Horizontal,
123-
})),
124-
WidgetHolder::new(Widget::NumberInput(NumberInput {
125-
value: Some({
126-
let NodeInput::Value {tagged_value: TaggedValue::F32(x), ..} = document_node.inputs[index] else {
127-
panic!("Add input should be f32")
128-
};
161+
];
129162

130-
x as f64
131-
}),
132-
mode: NumberInputMode::Increment,
133-
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
134-
NodeGraphMessage::SetInputValue {
135-
node: node_id,
136-
input_index: index,
137-
value: TaggedValue::F32(number_input.value.unwrap() as f32),
138-
}
139-
.into()
140-
}),
141-
..NumberInput::default()
142-
})),
143-
],
163+
if let NodeInput::Value {
164+
tagged_value: TaggedValue::F32(x),
165+
exposed: false,
166+
} = document_node.inputs[index]
167+
{
168+
widgets.extend_from_slice(&[
169+
WidgetHolder::new(Widget::Separator(Separator {
170+
separator_type: SeparatorType::Unrelated,
171+
direction: SeparatorDirection::Horizontal,
172+
})),
173+
WidgetHolder::new(Widget::NumberInput(NumberInput {
174+
value: Some(x as f64),
175+
mode: NumberInputMode::Increment,
176+
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
177+
NodeGraphMessage::SetInputValue {
178+
node: node_id,
179+
input_index: index,
180+
value: TaggedValue::F32(number_input.value.unwrap() as f32),
181+
}
182+
.into()
183+
}),
184+
..NumberInput::default()
185+
})),
186+
]);
187+
}
188+
189+
LayoutGroup::Row { widgets }
144190
};
145-
vec![operand("Left", 0), operand("Right", 1)]
191+
vec![operand("Input", 0), operand("Addend", 1)]
146192
}
147193

148194
fn unknown_node_properties(document_node: &DocumentNode) -> Vec<LayoutGroup> {

0 commit comments

Comments
 (0)