Add node description tooltips in the Properties panel and on secondary inputs in the graph (#2590)

Add tooltips to secondary inputs in graph/Properties panel, and to nodes in Properties panel
This commit is contained in:
Keavon Chambers 2025-04-17 00:59:46 -07:00 committed by GitHub
parent ab39f3f837
commit eca5d0d105
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 434 additions and 283 deletions

View file

@ -167,7 +167,6 @@ impl Dispatcher {
// Send the information for tooltips and categories for each node/input.
queue.add(FrontendMessage::SendUIMetadata {
input_type_descriptions: Vec::new(),
node_descriptions: document_node_definitions::collect_node_descriptions(),
node_types: document_node_definitions::collect_node_types(),
});

View file

@ -44,8 +44,6 @@ pub enum FrontendMessage {
// Send prefix: Send global, static data to the frontend that is never updated
SendUIMetadata {
#[serde(rename = "inputTypeDescriptions")]
input_type_descriptions: Vec<(String, String)>,
#[serde(rename = "nodeDescriptions")]
node_descriptions: Vec<(String, String)>,
#[serde(rename = "nodeTypes")]

View file

@ -325,7 +325,14 @@ pub enum LayoutGroup {
},
// TODO: Move this from being a child of `enum LayoutGroup` to being a child of `enum Layout`
#[serde(rename = "section")]
Section { name: String, visible: bool, pinned: bool, id: u64, layout: SubLayout },
Section {
name: String,
description: String,
visible: bool,
pinned: bool,
id: u64,
layout: SubLayout,
},
}
impl Default for LayoutGroup {
@ -402,6 +409,7 @@ impl LayoutGroup {
(
Self::Section {
name: current_name,
description: current_description,
visible: current_visible,
pinned: current_pinned,
id: current_id,
@ -409,6 +417,7 @@ impl LayoutGroup {
},
Self::Section {
name: new_name,
description: new_description,
visible: new_visible,
pinned: new_pinned,
id: new_id,
@ -417,9 +426,16 @@ impl LayoutGroup {
) => {
// Resend the entire panel if the lengths, names, visibility, or node IDs are different
// TODO: Diff insersion and deletion of items
if current_layout.len() != new_layout.len() || *current_name != new_name || *current_visible != new_visible || *current_pinned != new_pinned || *current_id != new_id {
if current_layout.len() != new_layout.len()
|| *current_name != new_name
|| *current_description != new_description
|| *current_visible != new_visible
|| *current_pinned != new_pinned
|| *current_id != new_id
{
// Update self to reflect new changes
current_name.clone_from(&new_name);
current_description.clone_from(&new_description);
*current_visible = new_visible;
*current_pinned = new_pinned;
*current_id = new_id;
@ -428,6 +444,7 @@ impl LayoutGroup {
// Push an update layout group to the diff
let new_value = DiffUpdate::LayoutGroup(Self::Section {
name: new_name,
description: new_description,
visible: new_visible,
pinned: new_pinned,
id: new_id,

View file

@ -353,7 +353,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
}
let Some(bounds) = self.metadata().bounding_box_document(layer) else { continue };
let name = self.network_interface.frontend_display_name(&layer.to_node(), &[]);
let name = self.network_interface.display_name(&layer.to_node(), &[]);
let transform = self.metadata().document_to_viewport
* DAffine2::from_translation(bounds[0].min(bounds[1]))

View file

@ -240,7 +240,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
responses.add(NodeGraphMessage::SetDisplayName {
node_id,
alias: network_interface.frontend_display_name(&artboard.to_node(), &[]),
alias: network_interface.display_name(&artboard.to_node(), &[]),
skip_adding_history_step: true,
});

View file

@ -120,7 +120,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
@ -141,7 +141,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Out".to_string()],
..Default::default()
},
@ -203,7 +203,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Graphical Data".into(), "Over".into()],
input_properties: vec![("Graphical Data", "TODO").into(), ("Over", "TODO").into()],
output_names: vec!["Out".to_string()],
node_type_metadata: NodeTypePersistentMetadata::layer(IVec2::new(0, 0)),
network_metadata: Some(NodeNetworkMetadata {
@ -316,10 +316,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec![
"Artboards".into(),
PropertiesRow::with_override("Contents", WidgetOverride::Hidden),
("Artboards", "TODO").into(),
PropertiesRow::with_override("Contents", "TODO", WidgetOverride::Hidden),
PropertiesRow::with_override(
"Location",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "X".to_string(),
y: "Y".to_string(),
@ -329,6 +330,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Dimensions",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "W".to_string(),
y: "H".to_string(),
@ -336,8 +338,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
),
PropertiesRow::with_override("Background", WidgetOverride::Custom("artboard_background".to_string())),
"Clip".into(),
PropertiesRow::with_override("Background", "TODO", WidgetOverride::Custom("artboard_background".to_string())),
("Clip", "TODO").into(),
],
output_names: vec!["Out".to_string()],
node_type_metadata: NodeTypePersistentMetadata::layer(IVec2::new(0, 0)),
@ -420,7 +422,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Empty".into(), "URL".into()],
input_properties: vec![("Empty", "TODO").into(), ("URL", "TODO").into()],
output_names: vec!["Image".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -570,7 +572,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Canvas".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -670,7 +672,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Artwork".into(), "Footprint".into()],
input_properties: vec![("Artwork", "TODO").into(), ("Footprint", "TODO").into()],
output_names: vec!["Canvas".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -730,7 +732,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into(), PropertiesRow::with_override("Stencil", WidgetOverride::Custom("mask_stencil".to_string()))],
input_properties: vec![
("Image", "TODO").into(),
PropertiesRow::with_override("Stencil", "TODO", WidgetOverride::Custom("mask_stencil".to_string())),
],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -754,7 +759,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into(), PropertiesRow::with_override("Insertion", WidgetOverride::Hidden), "Into".into()],
input_properties: vec![
("Image", "TODO").into(),
PropertiesRow::with_override("Insertion", "TODO", WidgetOverride::Hidden),
("Into", "TODO").into(),
],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -779,7 +788,13 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["None".into(), "Red".into(), "Green".into(), "Blue".into(), "Alpha".into()],
input_properties: vec![
("None", "TODO").into(),
("Red", "TODO").into(),
("Green", "TODO").into(),
("Blue", "TODO").into(),
("Alpha", "TODO").into(),
],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -848,7 +863,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into()],
input_properties: vec![("Image", "TODO").into()],
output_names: vec!["Red".to_string(), "Green".to_string(), "Blue".to_string(), "Alpha".to_string()],
has_primary_output: false,
network_metadata: Some(NodeNetworkMetadata {
@ -933,7 +948,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Vector2".into()],
input_properties: vec![("Vector2", "TODO").into()],
output_names: vec!["X".to_string(), "Y".to_string()],
has_primary_output: false,
network_metadata: Some(NodeNetworkMetadata {
@ -1003,7 +1018,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Background".into(), "Bounds".into(), "Trace".into(), "Cache".into()],
input_properties: vec![("Background", "TODO").into(), ("Bounds", "TODO").into(), ("Trace", "TODO").into(), ("Cache", "TODO").into()],
output_names: vec!["Image".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1040,7 +1055,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into()],
input_properties: vec![("Image", "TODO").into()],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -1059,7 +1074,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into()],
input_properties: vec![("Image", "TODO").into()],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -1093,7 +1108,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Empty".into(), "Image".into()],
input_properties: vec![("Empty", "TODO").into(), ("Image", "TODO").into()],
output_names: vec!["Image".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1156,7 +1171,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Uniform".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1235,7 +1250,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Storage".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1314,7 +1329,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into(), "In".into()],
input_properties: vec![("In", "TODO").into(), ("In", "TODO").into()],
output_names: vec!["Output Buffer".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1403,7 +1418,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into(), "In".into(), "In".into()],
input_properties: vec![("In", "TODO").into(), ("In", "TODO").into(), ("In", "TODO").into()],
output_names: vec!["Command Buffer".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1463,7 +1478,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Shader Handle".into(), "String".into(), "Bindgroup".into(), "Arc Shader Input".into()],
input_properties: vec![
("Shader Handle", "TODO").into(),
("String", "TODO").into(),
("Bindgroup", "TODO").into(),
("Arc Shader Input", "TODO").into(),
],
output_names: vec!["Pipeline Layout".to_string()],
..Default::default()
},
@ -1508,7 +1528,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Pipeline Result".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1588,7 +1608,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Buffer".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1733,7 +1753,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Texture".into(), "Surface".into()],
input_properties: vec![("Texture", "TODO").into(), ("Surface", "TODO").into()],
output_names: vec!["Rendered Texture".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1806,7 +1826,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["In".into()],
input_properties: vec![("In", "TODO").into()],
output_names: vec!["Texture".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -1864,7 +1884,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Image".into(), "Node".into()],
input_properties: vec![("Image", "TODO").into(), ("Node", "TODO").into()],
output_names: vec!["Image".to_string()],
..Default::default()
},
@ -1882,7 +1902,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Node".into()],
input_properties: vec![("Node", "TODO").into()],
output_names: vec!["Document Node".to_string()],
..Default::default()
},
@ -1909,10 +1929,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec![
"Image".into(),
PropertiesRow::with_override("Brightness", WidgetOverride::Custom("brightness".to_string())),
PropertiesRow::with_override("Brightness", WidgetOverride::Custom("contrast".to_string())),
"Use Classic".into(),
("Image", "TODO").into(),
PropertiesRow::with_override("Brightness", "TODO", WidgetOverride::Custom("brightness".to_string())),
PropertiesRow::with_override("Brightness", "TODO", WidgetOverride::Custom("contrast".to_string())),
("Use Classic", "TODO").into(),
],
output_names: vec!["Image".to_string()],
..Default::default()
@ -1938,7 +1958,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
// ..Default::default()
// },
// persistent_node_metadata: DocumentNodePersistentMetadata {
// input_properties: vec!["Image".into(), "Curve".into()],
// input_properties: vec![("Image", "TODO").into(), ("Curve", "TODO").into()],
// output_names: vec!["Image".to_string()],
// ..Default::default()
// },
@ -1963,9 +1983,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec![
"None".into(),
("None", "TODO").into(),
PropertiesRow::with_override(
"Start",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "X".to_string(),
y: "Y".to_string(),
@ -1975,6 +1996,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"End",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "X".to_string(),
y: "Y".to_string(),
@ -2025,7 +2047,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec!["Vector Data".into(), "Modification".into()],
input_properties: vec![("Vector Data", "TODO").into(), ("Modification", "TODO").into()],
output_names: vec!["Vector Data".to_string()],
network_metadata: Some(NodeNetworkMetadata {
persistent_metadata: NodeNetworkPersistentMetadata {
@ -2085,11 +2107,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec![
"Editor API".into(),
PropertiesRow::with_override("Text", WidgetOverride::Custom("text_area".to_string())),
PropertiesRow::with_override("Font", WidgetOverride::Custom("text_font".to_string())),
("Editor API", "TODO").into(),
PropertiesRow::with_override("Text", "TODO", WidgetOverride::Custom("text_area".to_string())),
PropertiesRow::with_override("Font", "TODO", WidgetOverride::Custom("text_font".to_string())),
PropertiesRow::with_override(
"Size",
"TODO",
WidgetOverride::Number(NumberInputSettings {
unit: Some(" px".to_string()),
min: Some(1.),
@ -2098,6 +2121,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Line Height",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
step: Some(0.1),
@ -2106,6 +2130,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Character Spacing",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
step: Some(0.1),
@ -2114,6 +2139,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Max Width",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(1.),
blank_assist: false,
@ -2122,6 +2148,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Max Height",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(1.),
blank_assist: false,
@ -2211,9 +2238,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
input_properties: vec![
"Vector Data".into(),
("Vector Data", "TODO").into(),
PropertiesRow::with_override(
"Translation",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "X".to_string(),
y: "Y".to_string(),
@ -2221,9 +2249,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
),
PropertiesRow::with_override("Rotation", WidgetOverride::Custom("transform_rotation".to_string())),
PropertiesRow::with_override("Rotation", "TODO", WidgetOverride::Custom("transform_rotation".to_string())),
PropertiesRow::with_override(
"Scale",
"TODO",
WidgetOverride::Vec2(Vec2InputSettings {
x: "W".to_string(),
y: "H".to_string(),
@ -2231,8 +2260,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
),
PropertiesRow::with_override("Skew", WidgetOverride::Custom("transform_skew".to_string())),
PropertiesRow::with_override("Pivot", WidgetOverride::Hidden),
PropertiesRow::with_override("Skew", "TODO", WidgetOverride::Custom("transform_skew".to_string())),
PropertiesRow::with_override("Pivot", "TODO", WidgetOverride::Hidden),
],
output_names: vec!["Data".to_string()],
..Default::default()
@ -2332,7 +2361,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
..Default::default()
}),
input_properties: vec!["Group of Paths".into(), "Operation".into()],
input_properties: vec![("Group of Paths", "TODO").into(), ("Operation", "TODO").into()],
output_names: vec!["Vector".to_string()],
..Default::default()
},
@ -2363,10 +2392,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
},
persistent_node_metadata: DocumentNodePersistentMetadata {
input_properties: vec![
"Points".into(),
Into::<PropertiesRow>::into("Instance").with_tooltip("Artwork to be copied and placed at each point"),
("Points", "TODO").into(),
Into::<PropertiesRow>::into(("Instance", "TODO")).with_tooltip("Artwork to be copied and placed at each point"),
PropertiesRow::with_override(
"Random Scale Min",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
mode: NumberInputMode::Range,
@ -2379,6 +2409,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Minimum range of randomized sizes given to each instance"),
PropertiesRow::with_override(
"Random Scale Max",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
mode: NumberInputMode::Range,
@ -2391,6 +2422,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Minimum range of randomized sizes given to each instance"),
PropertiesRow::with_override(
"Random Scale Bias",
"TODO",
WidgetOverride::Number(NumberInputSettings {
mode: NumberInputMode::Range,
range_min: Some(-50.),
@ -2401,6 +2433,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Bias for the probability distribution of randomized sizes (0 is uniform, negatives favor more of small sizes, positives favor more of large sizes)"),
PropertiesRow::with_override(
"Random Scale Seed",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
is_integer: true,
@ -2410,6 +2443,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Seed to determine unique variations on all the randomized instance sizes"),
PropertiesRow::with_override(
"Random Rotation",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
max: Some(360.),
@ -2421,6 +2455,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Range of randomized angles given to each instance, in degrees ranging from furthest clockwise to counterclockwise"),
PropertiesRow::with_override(
"Random Rotation Seed",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
is_integer: true,
@ -2552,9 +2587,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
input_properties: vec![
"Vector Data".into(),
("Vector Data", "TODO").into(),
PropertiesRow::with_override(
"Spacing",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(1.),
unit: Some(" px".to_string()),
@ -2564,6 +2600,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Distance between each instance (exact if 'Adaptive Spacing' is disabled, approximate if enabled)"),
PropertiesRow::with_override(
"Start Offset",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
unit: Some(" px".to_string()),
@ -2573,6 +2610,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
.with_tooltip("Exclude some distance from the start of the path before the first instance"),
PropertiesRow::with_override(
"Stop Offset",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
unit: Some(" px".to_string()),
@ -2580,7 +2618,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
}),
)
.with_tooltip("Exclude some distance from the end of the path after the last instance"),
Into::<PropertiesRow>::into("Adaptive Spacing").with_tooltip("Round 'Spacing' to a nearby value that divides into the path length evenly"),
Into::<PropertiesRow>::into(("Adaptive Spacing", "TODO")).with_tooltip("Round 'Spacing' to a nearby value that divides into the path length evenly"),
],
output_names: vec!["Vector".to_string()],
..Default::default()
@ -2686,9 +2724,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
..Default::default()
}),
input_properties: vec![
"Vector Data".into(),
("Vector Data", "TODO").into(),
PropertiesRow::with_override(
"Separation Disk Diameter",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.01),
mode: NumberInputMode::Range,
@ -2699,6 +2738,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
),
PropertiesRow::with_override(
"Seed",
"TODO",
WidgetOverride::Number(NumberInputSettings {
min: Some(0.),
is_integer: true,
@ -2800,10 +2840,10 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
input_properties: fields
.iter()
.map(|f| match f.widget_override {
RegistryWidgetOverride::None => f.name.into(),
RegistryWidgetOverride::Hidden => PropertiesRow::with_override(f.name, WidgetOverride::Hidden),
RegistryWidgetOverride::String(str) => PropertiesRow::with_override(f.name, WidgetOverride::String(str.to_string())),
RegistryWidgetOverride::Custom(str) => PropertiesRow::with_override(f.name, WidgetOverride::Custom(str.to_string())),
RegistryWidgetOverride::None => (f.name, f.description).into(),
RegistryWidgetOverride::Hidden => PropertiesRow::with_override(f.name, f.description, WidgetOverride::Hidden),
RegistryWidgetOverride::String(str) => PropertiesRow::with_override(f.name, f.description, WidgetOverride::String(str.to_string())),
RegistryWidgetOverride::Custom(str) => PropertiesRow::with_override(f.name, f.description, WidgetOverride::Custom(str.to_string())),
})
.collect(),
output_names: vec![output_type.to_string()],
@ -2994,7 +3034,7 @@ fn static_input_properties() -> InputProperties {
map.insert(
"number".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let mut number_input = NumberInput::default();
if let Some(unit) = context
.network_interface
@ -3060,14 +3100,14 @@ fn static_input_properties() -> InputProperties {
true
});
Ok(vec![LayoutGroup::Row {
widgets: node_properties::number_widget(document_node, node_id, index, input_name, number_input, blank_assist),
widgets: node_properties::number_widget(document_node, node_id, index, input_name, input_description, number_input, blank_assist),
}])
}),
);
map.insert(
"vec2".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let x = context
.network_interface
.input_metadata(&node_id, index, "x", context.selection_network_path)
@ -3102,6 +3142,7 @@ fn static_input_properties() -> InputProperties {
node_id,
index,
input_name,
input_description,
x,
y,
unit,
@ -3113,39 +3154,48 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_scale".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let scale = node_properties::number_widget(document_node, node_id, index, input_name, NumberInput::default().min(0.).disabled(!coherent_noise_active), true);
let scale = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default().min(0.).disabled(!coherent_noise_active),
true,
);
Ok(vec![scale.into()])
}),
);
map.insert(
"noise_properties_noise_type".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let noise_type_row = node_properties::noise_type(document_node, node_id, index, input_name, true);
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let noise_type_row = node_properties::noise_type(document_node, node_id, index, input_name, input_description, true);
Ok(vec![noise_type_row, LayoutGroup::Row { widgets: Vec::new() }])
}),
);
map.insert(
"noise_properties_domain_warp_type".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let domain_warp_type = node_properties::domain_warp_type(document_node, node_id, index, input_name, true, !coherent_noise_active);
let domain_warp_type = node_properties::domain_warp_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active);
Ok(vec![domain_warp_type])
}),
);
map.insert(
"noise_properties_domain_warp_amplitude".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, _, _, domain_warp_active, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let domain_warp_amplitude = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default().min(0.).disabled(!coherent_noise_active || !domain_warp_active),
true,
);
@ -3155,22 +3205,23 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_fractal_type".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, _, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_type_row = node_properties::fractal_type(document_node, node_id, index, input_name, true, !coherent_noise_active);
let fractal_type_row = node_properties::fractal_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active);
Ok(vec![fractal_type_row])
}),
);
map.insert(
"noise_properties_fractal_octaves".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_octaves = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.min(1.)
@ -3186,13 +3237,14 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_fractal_lacunarity".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_lacunarity = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.min(0.)
@ -3206,13 +3258,14 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_fractal_gain".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_gain = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.min(0.)
@ -3226,13 +3279,14 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_fractal_weighted_strength".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (fractal_active, coherent_noise_active, _, _, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_weighted_strength = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.min(0.)
@ -3246,13 +3300,14 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_ping_pong_strength".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (fractal_active, coherent_noise_active, _, ping_pong_active, _, domain_warp_only_fractal_type_wrongly_active) = node_properties::query_noise_pattern_state(node_id, context)?;
let fractal_ping_pong_strength = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.min(0.)
@ -3266,31 +3321,33 @@ fn static_input_properties() -> InputProperties {
map.insert(
"noise_properties_cellular_distance_function".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let cellular_distance_function_row = node_properties::cellular_distance_function(document_node, node_id, index, input_name, true, !coherent_noise_active || !cellular_noise_active);
let cellular_distance_function_row =
node_properties::cellular_distance_function(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active || !cellular_noise_active);
Ok(vec![cellular_distance_function_row])
}),
);
map.insert(
"noise_properties_cellular_return_type".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let cellular_return_type = node_properties::cellular_return_type(document_node, node_id, index, input_name, true, !coherent_noise_active || !cellular_noise_active);
let cellular_return_type = node_properties::cellular_return_type(document_node, node_id, index, input_name, input_description, true, !coherent_noise_active || !cellular_noise_active);
Ok(vec![cellular_return_type])
}),
);
map.insert(
"noise_properties_cellular_jitter".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (_, coherent_noise_active, cellular_noise_active, _, _, _) = node_properties::query_noise_pattern_state(node_id, context)?;
let cellular_jitter = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default()
.mode_range()
.range_min(Some(0.))
@ -3304,7 +3361,7 @@ fn static_input_properties() -> InputProperties {
map.insert(
"brightness".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let is_use_classic = document_node
.inputs
.iter()
@ -3319,6 +3376,7 @@ fn static_input_properties() -> InputProperties {
node_id,
index,
input_name,
input_description,
NumberInput::default().mode_range().range_min(Some(b_min)).range_max(Some(b_max)).unit("%").display_decimal_places(2),
true,
);
@ -3328,7 +3386,7 @@ fn static_input_properties() -> InputProperties {
map.insert(
"contrast".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let is_use_classic = document_node
.inputs
.iter()
@ -3343,6 +3401,7 @@ fn static_input_properties() -> InputProperties {
node_id,
index,
input_name,
input_description,
NumberInput::default().mode_range().range_min(Some(c_min)).range_max(Some(c_max)).unit("%").display_decimal_places(2),
true,
);
@ -3352,52 +3411,68 @@ fn static_input_properties() -> InputProperties {
map.insert(
"assign_colors_gradient".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let gradient_row = node_properties::color_widget(document_node, node_id, index, input_name, ColorInput::default().allow_none(false), true);
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let gradient_row = node_properties::color_widget(document_node, node_id, index, input_name, input_description, ColorInput::default().allow_none(false), true);
Ok(vec![gradient_row])
}),
);
map.insert(
"assign_colors_seed".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
let seed_row = node_properties::number_widget(document_node, node_id, index, input_name, NumberInput::default().min(0.).int().disabled(!randomize_enabled), true);
let seed_row = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default().min(0.).int().disabled(!randomize_enabled),
true,
);
Ok(vec![seed_row.into()])
}),
);
map.insert(
"assign_colors_repeat_every".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let randomize_enabled = node_properties::query_assign_colors_randomize(node_id, context)?;
let repeat_every_row = node_properties::number_widget(document_node, node_id, index, input_name, NumberInput::default().min(0.).int().disabled(randomize_enabled), true);
let repeat_every_row = node_properties::number_widget(
document_node,
node_id,
index,
input_name,
input_description,
NumberInput::default().min(0.).int().disabled(randomize_enabled),
true,
);
Ok(vec![repeat_every_row.into()])
}),
);
map.insert(
"mask_stencil".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let mask = node_properties::color_widget(document_node, node_id, index, input_name, ColorInput::default(), true);
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let mask = node_properties::color_widget(document_node, node_id, index, input_name, input_description, ColorInput::default(), true);
Ok(vec![mask])
}),
);
map.insert(
"spline_input".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
Ok(vec![LayoutGroup::Row {
widgets: node_properties::vec_dvec2_input(document_node, node_id, index, input_name, TextInput::default().centered(true), true),
widgets: node_properties::vec_dvec2_input(document_node, node_id, index, input_name, input_description, TextInput::default().centered(true), true),
}])
}),
);
map.insert(
"transform_rotation".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, super::utility_types::FrontendGraphDataType::Number, true);
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, input_description, super::utility_types::FrontendGraphDataType::Number, true);
let Some(input) = document_node.inputs.get(index) else {
return Err("Input not found in transform rotation input override".to_string());
@ -3427,9 +3502,9 @@ fn static_input_properties() -> InputProperties {
map.insert(
"transform_skew".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, super::utility_types::FrontendGraphDataType::Number, true);
let mut widgets = node_properties::start_widgets(document_node, node_id, index, input_name, input_description, super::utility_types::FrontendGraphDataType::Number, true);
let Some(input) = document_node.inputs.get(index) else {
return Err("Input not found in transform skew input override".to_string());
@ -3472,17 +3547,17 @@ fn static_input_properties() -> InputProperties {
map.insert(
"text_area".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
Ok(vec![LayoutGroup::Row {
widgets: node_properties::text_area_widget(document_node, node_id, index, input_name, true),
widgets: node_properties::text_area_widget(document_node, node_id, index, input_name, input_description, true),
}])
}),
);
map.insert(
"text_font".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (font, style) = node_properties::font_inputs(document_node, node_id, index, input_name, true);
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
let (font, style) = node_properties::font_inputs(document_node, node_id, index, input_name, input_description, true);
let mut result = vec![LayoutGroup::Row { widgets: font }];
if let Some(style) = style {
result.push(LayoutGroup::Row { widgets: style });
@ -3493,12 +3568,13 @@ fn static_input_properties() -> InputProperties {
map.insert(
"artboard_background".to_string(),
Box::new(|node_id, index, context| {
let (document_node, input_name) = node_properties::query_node_and_input_name(node_id, index, context)?;
let (document_node, input_name, input_description) = node_properties::query_node_and_input_info(node_id, index, context)?;
Ok(vec![node_properties::color_widget(
document_node,
node_id,
index,
input_name,
input_description,
ColorInput::default().allow_none(false),
true,
)])

View file

@ -2051,7 +2051,7 @@ impl NodeGraphMessageHandler {
Separator::new(SeparatorType::Related).widget_holder(),
IconLabel::new("Layer").tooltip("Name of the selected layer").widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
TextInput::new(context.network_interface.frontend_display_name(&layer, context.selection_network_path))
TextInput::new(context.network_interface.display_name(&layer, context.selection_network_path))
.tooltip("Name of the selected layer")
.on_update(move |text_input| {
NodeGraphMessage::SetDisplayName {
@ -2095,9 +2095,10 @@ impl NodeGraphMessageHandler {
!context.network_interface.is_layer(node_id, context.selection_network_path)
}
})
.map(|(_, node_id)| node_id)
.collect::<Vec<_>>()
.iter()
.map(|(_, node_id)| node_properties::generate_node_properties(*node_id, context))
.into_iter()
.map(|node_id| node_properties::generate_node_properties(node_id, context))
.collect::<Vec<_>>();
layer_properties.extend(node_properties);
@ -2215,6 +2216,7 @@ impl NodeGraphMessageHandler {
resolved_type: Some(format!("{:?} from {:?}", &input.ty, input.type_source)),
valid_types: input.valid_types.iter().map(|ty| ty.to_string()).collect(),
name: input.name.unwrap_or_else(|| input.ty.nested_type().to_string()),
description: input.description.unwrap_or_default(),
connected_to: input.output_connector,
})
});
@ -2234,6 +2236,7 @@ impl NodeGraphMessageHandler {
Some(FrontendGraphOutput {
data_type: frontend_data_type,
name: "Output 1".to_string(),
description: String::new(),
resolved_type: primary_output_type.map(|(input, type_source)| format!("{input:?} from {type_source:?}")),
connected_to,
})
@ -2267,6 +2270,7 @@ impl NodeGraphMessageHandler {
exposed_outputs.push(FrontendGraphOutput {
data_type: frontend_data_type,
name: output_name,
description: String::new(),
resolved_type: exposed_output.clone().map(|(input, type_source)| format!("{input:?} from {type_source:?}")),
connected_to,
});
@ -2306,7 +2310,7 @@ impl NodeGraphMessageHandler {
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
can_be_layer: can_be_layer_lookup.contains(&node_id),
reference: network_interface.reference(&node_id, breadcrumb_network_path).cloned().unwrap_or_default(),
display_name: network_interface.frontend_display_name(&node_id, breadcrumb_network_path),
display_name: network_interface.display_name(&node_id, breadcrumb_network_path),
primary_input,
exposed_inputs,
primary_output,
@ -2332,7 +2336,7 @@ impl NodeGraphMessageHandler {
if let Some(network) = node.implementation.get_network() {
current_network = network;
};
subgraph_names.push(network_interface.frontend_display_name(node_id, &current_network_path));
subgraph_names.push(network_interface.display_name(node_id, &current_network_path));
current_network_path.push(*node_id)
}
Some(subgraph_names)
@ -2395,7 +2399,7 @@ impl NodeGraphMessageHandler {
let data = LayerPanelEntry {
id: node_id,
alias: network_interface.frontend_display_name(&node_id, &[]),
alias: network_interface.display_name(&node_id, &[]),
tooltip: if cfg!(debug_assertions) { format!("Layer ID: {node_id}") } else { "".into() },
in_selected_network: selection_network_path.is_empty(),
children_allowed,
@ -2522,6 +2526,7 @@ impl NodeGraphMessageHandler {
#[derive(Default)]
struct InputLookup {
name: Option<String>,
description: Option<String>,
ty: Type,
type_source: TypeSource,
valid_types: Vec<Type>,
@ -2552,11 +2557,16 @@ fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface:
.input_name(&node_id, index, breadcrumb_network_path)
.filter(|s| !s.is_empty())
.map(|name| name.to_string());
let description = network_interface
.input_description(&node_id, index, breadcrumb_network_path)
.filter(|s| !s.is_empty())
.map(|description| description.to_string());
// Get the output connector that feeds into this input (done here as well for simplicity)
let connector = OutputConnector::from_input(input);
inputs.push(Some(InputLookup {
name,
description,
output_connector: connector,
..Default::default()
}));

View file

@ -73,12 +73,12 @@ pub fn add_blank_assist(widgets: &mut Vec<WidgetHolder>) {
]);
}
pub fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, data_type: FrontendGraphDataType, blank_assist: bool) -> Vec<WidgetHolder> {
pub fn start_widgets(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, data_type: FrontendGraphDataType, blank_assist: bool) -> Vec<WidgetHolder> {
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return vec![];
};
let mut widgets = vec![expose_widget(node_id, index, data_type, input.is_exposed()), TextLabel::new(name).widget_holder()];
let mut widgets = vec![expose_widget(node_id, index, data_type, input.is_exposed()), TextLabel::new(name).tooltip(description).widget_holder()];
if blank_assist {
add_blank_assist(&mut widgets);
}
@ -97,6 +97,10 @@ pub(crate) fn property_from_type(
log::warn!("A widget failed to be built for node {node_id}, index {index} because the input name could not be determined");
return Err(vec![]);
};
let Some(description) = context.network_interface.input_description(&node_id, index, context.selection_network_path) else {
log::warn!("A widget failed to be built for node {node_id}, index {index} because the input description could not be determined");
return Err(vec![]);
};
let Some(network) = context.network_interface.nested_network(context.selection_network_path) else {
log::warn!("A widget failed to be built for node {node_id}, index {index} because the network could not be determined");
return Err(vec![]);
@ -122,59 +126,72 @@ pub(crate) fn property_from_type(
Type::Concrete(concrete_type) => {
match concrete_type.alias.as_ref().map(|x| x.as_ref()) {
// Aliased types (ambiguous values)
Some("Percentage") => number_widget(document_node, node_id, index, name, number_input.percentage().min(min(0.)).max(max(100.)), true).into(),
Some("SignedPercentage") => number_widget(document_node, node_id, index, name, number_input.percentage().min(min(-100.)).max(max(100.)), true).into(),
Some("Angle") => number_widget(document_node, node_id, index, name, number_input.mode_range().min(min(-180.)).max(max(180.)).unit("°"), true).into(),
Some("PixelLength") => number_widget(document_node, node_id, index, name, number_input.min(min(0.)).unit(" px"), true).into(),
Some("Length") => number_widget(document_node, node_id, index, name, number_input.min(min(0.)), true).into(),
Some("Fraction") => number_widget(document_node, node_id, index, name, number_input.mode_range().min(min(0.)).max(max(1.)), true).into(),
Some("IntegerCount") => number_widget(document_node, node_id, index, name, number_input.int().min(min(1.)), true).into(),
Some("SeedValue") => number_widget(document_node, node_id, index, name, number_input.int().min(min(0.)), true).into(),
Some("Resolution") => vec2_widget(document_node, node_id, index, name, "W", "H", " px", Some(64.), add_blank_assist),
Some("Percentage") => number_widget(document_node, node_id, index, name, description, number_input.percentage().min(min(0.)).max(max(100.)), true).into(),
Some("SignedPercentage") => number_widget(document_node, node_id, index, name, description, number_input.percentage().min(min(-100.)).max(max(100.)), true).into(),
Some("Angle") => number_widget(
document_node,
node_id,
index,
name,
description,
number_input.mode_range().min(min(-180.)).max(max(180.)).unit("°"),
true,
)
.into(),
Some("PixelLength") => number_widget(document_node, node_id, index, name, description, number_input.min(min(0.)).unit(" px"), true).into(),
Some("Length") => number_widget(document_node, node_id, index, name, description, number_input.min(min(0.)), true).into(),
Some("Fraction") => number_widget(document_node, node_id, index, name, description, number_input.mode_range().min(min(0.)).max(max(1.)), true).into(),
Some("IntegerCount") => number_widget(document_node, node_id, index, name, description, number_input.int().min(min(1.)), true).into(),
Some("SeedValue") => number_widget(document_node, node_id, index, name, description, number_input.int().min(min(0.)), true).into(),
Some("Resolution") => vec2_widget(document_node, node_id, index, name, description, "W", "H", " px", Some(64.), add_blank_assist),
// For all other types, use TypeId-based matching
_ => {
use std::any::TypeId;
match concrete_type.id {
Some(x) if x == TypeId::of::<bool>() => bool_widget(document_node, node_id, index, name, CheckboxInput::default(), true).into(),
Some(x) if x == TypeId::of::<f64>() => number_widget(document_node, node_id, index, name, number_input.min(min(f64::NEG_INFINITY)).max(max(f64::INFINITY)), true).into(),
Some(x) if x == TypeId::of::<u32>() => number_widget(document_node, node_id, index, name, number_input.int().min(min(0.)).max(max(f64::from(u32::MAX))), true).into(),
Some(x) if x == TypeId::of::<u64>() => number_widget(document_node, node_id, index, name, number_input.int().min(min(0.)), true).into(),
Some(x) if x == TypeId::of::<String>() => text_widget(document_node, node_id, index, name, true).into(),
Some(x) if x == TypeId::of::<Color>() => color_widget(document_node, node_id, index, name, ColorInput::default().allow_none(false), true),
Some(x) if x == TypeId::of::<Option<Color>>() => color_widget(document_node, node_id, index, name, ColorInput::default().allow_none(true), true),
Some(x) if x == TypeId::of::<DVec2>() => vec2_widget(document_node, node_id, index, name, "X", "Y", "", None, add_blank_assist),
Some(x) if x == TypeId::of::<UVec2>() => vec2_widget(document_node, node_id, index, name, "X", "Y", "", Some(0.), add_blank_assist),
Some(x) if x == TypeId::of::<IVec2>() => vec2_widget(document_node, node_id, index, name, "X", "Y", "", None, add_blank_assist),
Some(x) if x == TypeId::of::<Vec<f64>>() => vec_f64_input(document_node, node_id, index, name, TextInput::default(), true).into(),
Some(x) if x == TypeId::of::<Vec<DVec2>>() => vec_dvec2_input(document_node, node_id, index, name, TextInput::default(), true).into(),
Some(x) if x == TypeId::of::<bool>() => bool_widget(document_node, node_id, index, name, description, CheckboxInput::default(), true).into(),
Some(x) if x == TypeId::of::<f64>() => {
number_widget(document_node, node_id, index, name, description, number_input.min(min(f64::NEG_INFINITY)).max(max(f64::INFINITY)), true).into()
}
Some(x) if x == TypeId::of::<u32>() => {
number_widget(document_node, node_id, index, name, description, number_input.int().min(min(0.)).max(max(f64::from(u32::MAX))), true).into()
}
Some(x) if x == TypeId::of::<u64>() => number_widget(document_node, node_id, index, name, description, number_input.int().min(min(0.)), true).into(),
Some(x) if x == TypeId::of::<String>() => text_widget(document_node, node_id, index, name, description, true).into(),
Some(x) if x == TypeId::of::<Color>() => color_widget(document_node, node_id, index, name, description, ColorInput::default().allow_none(false), true),
Some(x) if x == TypeId::of::<Option<Color>>() => color_widget(document_node, node_id, index, name, description, ColorInput::default().allow_none(true), true),
Some(x) if x == TypeId::of::<DVec2>() => vec2_widget(document_node, node_id, index, name, description, "X", "Y", "", None, add_blank_assist),
Some(x) if x == TypeId::of::<UVec2>() => vec2_widget(document_node, node_id, index, name, description, "X", "Y", "", Some(0.), add_blank_assist),
Some(x) if x == TypeId::of::<IVec2>() => vec2_widget(document_node, node_id, index, name, description, "X", "Y", "", None, add_blank_assist),
Some(x) if x == TypeId::of::<Vec<f64>>() => vec_f64_input(document_node, node_id, index, name, description, TextInput::default(), true).into(),
Some(x) if x == TypeId::of::<Vec<DVec2>>() => vec_dvec2_input(document_node, node_id, index, name, description, TextInput::default(), true).into(),
Some(x) if x == TypeId::of::<Font>() => {
let (font_widgets, style_widgets) = font_inputs(document_node, node_id, index, name, false);
let (font_widgets, style_widgets) = font_inputs(document_node, node_id, index, name, description, false);
font_widgets.into_iter().chain(style_widgets.unwrap_or_default()).collect::<Vec<_>>().into()
}
Some(x) if x == TypeId::of::<Curve>() => curves_widget(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<GradientStops>() => color_widget(document_node, node_id, index, name, ColorInput::default().allow_none(false), true),
Some(x) if x == TypeId::of::<VectorDataTable>() => vector_widget(document_node, node_id, index, name, true).into(),
Some(x) if x == TypeId::of::<Curve>() => curves_widget(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<GradientStops>() => color_widget(document_node, node_id, index, name, description, ColorInput::default().allow_none(false), true),
Some(x) if x == TypeId::of::<VectorDataTable>() => vector_widget(document_node, node_id, index, name, description, true).into(),
Some(x) if x == TypeId::of::<RasterFrame>() || x == TypeId::of::<ImageFrameTable<Color>>() || x == TypeId::of::<TextureFrameTable>() => {
raster_widget(document_node, node_id, index, name, true).into()
raster_widget(document_node, node_id, index, name, description, true).into()
}
Some(x) if x == TypeId::of::<GraphicGroupTable>() => group_widget(document_node, node_id, index, name, true).into(),
Some(x) if x == TypeId::of::<GraphicGroupTable>() => group_widget(document_node, node_id, index, name, description, true).into(),
Some(x) if x == TypeId::of::<Footprint>() => {
let widgets = footprint_widget(document_node, node_id, index);
let (last, rest) = widgets.split_last().expect("Footprint widget should return multiple rows");
extra_widgets = rest.to_vec();
last.clone()
}
Some(x) if x == TypeId::of::<BlendMode>() => blend_mode(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RealTimeMode>() => real_time_mode(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RedGreenBlue>() => color_channel(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<RedGreenBlueAlpha>() => rgba_channel(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<XY>() => xy_components(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<NoiseType>() => noise_type(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<FractalType>() => fractal_type(document_node, node_id, index, name, true, false),
Some(x) if x == TypeId::of::<CellularDistanceFunction>() => cellular_distance_function(document_node, node_id, index, name, true, false),
Some(x) if x == TypeId::of::<CellularReturnType>() => cellular_return_type(document_node, node_id, index, name, true, false),
Some(x) if x == TypeId::of::<DomainWarpType>() => domain_warp_type(document_node, node_id, index, name, true, false),
Some(x) if x == TypeId::of::<BlendMode>() => blend_mode(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<RealTimeMode>() => real_time_mode(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<RedGreenBlue>() => color_channel(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<RedGreenBlueAlpha>() => rgba_channel(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<XY>() => xy_components(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<NoiseType>() => noise_type(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<FractalType>() => fractal_type(document_node, node_id, index, name, description, true, false),
Some(x) if x == TypeId::of::<CellularDistanceFunction>() => cellular_distance_function(document_node, node_id, index, name, description, true, false),
Some(x) if x == TypeId::of::<CellularReturnType>() => cellular_return_type(document_node, node_id, index, name, description, true, false),
Some(x) if x == TypeId::of::<DomainWarpType>() => domain_warp_type(document_node, node_id, index, name, description, true, false),
Some(x) if x == TypeId::of::<RelativeAbsolute>() => vec![
DropdownInput::new(vec![vec![
MenuListEntry::new("Relative")
@ -187,9 +204,9 @@ pub(crate) fn property_from_type(
.widget_holder(),
]
.into(),
Some(x) if x == TypeId::of::<GridType>() => grid_type_widget(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<LineCap>() => line_cap_widget(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<LineJoin>() => line_join_widget(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<GridType>() => grid_type_widget(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<LineCap>() => line_cap_widget(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<LineJoin>() => line_join_widget(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<FillType>() => vec![
DropdownInput::new(vec![vec![
MenuListEntry::new("Solid")
@ -214,9 +231,9 @@ pub(crate) fn property_from_type(
.widget_holder(),
]
.into(),
Some(x) if x == TypeId::of::<BooleanOperation>() => boolean_operation_radio_buttons(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<BooleanOperation>() => boolean_operation_radio_buttons(document_node, node_id, index, name, description, true),
Some(x) if x == TypeId::of::<CentroidType>() => centroid_widget(document_node, node_id, index),
Some(x) if x == TypeId::of::<LuminanceCalculation>() => luminance_calculation(document_node, node_id, index, name, true),
Some(x) if x == TypeId::of::<LuminanceCalculation>() => luminance_calculation(document_node, node_id, index, name, description, true),
// Some(x) if x == TypeId::of::<ImaginateSamplingMethod>() => vec![
// DropdownInput::new(
// ImaginateSamplingMethod::list()
@ -250,7 +267,7 @@ pub(crate) fn property_from_type(
// ]
// .into(),
_ => {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, true);
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, true);
widgets.extend_from_slice(&[
Separator::new(SeparatorType::Unrelated).widget_holder(),
TextLabel::new("-")
@ -277,8 +294,8 @@ pub(crate) fn property_from_type(
Ok(extra_widgets)
}
pub fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -296,8 +313,8 @@ pub fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
widgets
}
pub fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -315,8 +332,8 @@ pub fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: us
widgets
}
pub fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, checkbox_input: CheckboxInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, checkbox_input: CheckboxInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -336,7 +353,7 @@ pub fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
}
pub fn footprint_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> Vec<LayoutGroup> {
let mut location_widgets = start_widgets(document_node, node_id, index, "Footprint", FrontendGraphDataType::General, true);
let mut location_widgets = start_widgets(document_node, node_id, index, "Footprint", "TODO", FrontendGraphDataType::General, true);
location_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
let mut scale_widgets = vec![TextLabel::new("").widget_holder()];
@ -481,13 +498,14 @@ pub fn vec2_widget(
node_id: NodeId,
index: usize,
name: &str,
description: &str,
x: &str,
y: &str,
unit: &str,
min: Option<f64>,
mut assist: impl FnMut(&mut Vec<WidgetHolder>),
) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, false);
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Number, false);
assist(&mut widgets);
@ -598,8 +616,8 @@ pub fn vec2_widget(
LayoutGroup::Row { widgets }
}
pub fn vec_f64_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, text_input: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
pub fn vec_f64_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, text_input: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Number, blank_assist);
let from_string = |string: &str| {
string
@ -627,8 +645,8 @@ pub fn vec_f64_input(document_node: &DocumentNode, node_id: NodeId, index: usize
widgets
}
pub fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, text_props: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
pub fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, text_props: TextInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Number, blank_assist);
let from_string = |string: &str| {
string
@ -656,8 +674,8 @@ pub fn vec_dvec2_input(document_node: &DocumentNode, node_id: NodeId, index: usi
widgets
}
pub fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> (Vec<WidgetHolder>, Option<Vec<WidgetHolder>>) {
let mut first_widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> (Vec<WidgetHolder>, Option<Vec<WidgetHolder>>) {
let mut first_widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let mut second_widgets = None;
let from_font_input = |font: &FontInput| TaggedValue::Font(Font::new(font.font_family.clone(), font.font_style.clone()));
@ -690,8 +708,8 @@ pub fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize,
(first_widgets, second_widgets)
}
pub fn vector_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::VectorData, blank_assist);
pub fn vector_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::VectorData, blank_assist);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Vector data is supplied through the node graph").widget_holder());
@ -699,8 +717,8 @@ pub fn vector_widget(document_node: &DocumentNode, node_id: NodeId, index: usize
widgets
}
pub fn raster_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Raster, blank_assist);
pub fn raster_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Raster, blank_assist);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Raster data is supplied through the node graph").widget_holder());
@ -708,8 +726,8 @@ pub fn raster_widget(document_node: &DocumentNode, node_id: NodeId, index: usize
widgets
}
pub fn group_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Group, blank_assist);
pub fn group_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Group, blank_assist);
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
widgets.push(TextLabel::new("Group data is supplied through the node graph").widget_holder());
@ -717,8 +735,8 @@ pub fn group_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
widgets
}
pub fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, number_props: NumberInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
pub fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, number_props: NumberInput, blank_assist: bool) -> Vec<WidgetHolder> {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::Number, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -786,8 +804,8 @@ pub fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -813,8 +831,8 @@ pub fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
}
pub fn real_time_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn real_time_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -847,8 +865,8 @@ pub fn real_time_mode(document_node: &DocumentNode, node_id: NodeId, index: usiz
LayoutGroup::Row { widgets }.with_tooltip("Real Time Mode")
}
pub fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -874,8 +892,8 @@ pub fn rgba_channel(document_node: &DocumentNode, node_id: NodeId, index: usize,
LayoutGroup::Row { widgets }.with_tooltip("Color Channel")
}
pub fn xy_components(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn xy_components(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -902,8 +920,8 @@ pub fn xy_components(document_node: &DocumentNode, node_id: NodeId, index: usize
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -928,8 +946,8 @@ pub fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, n
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -954,8 +972,8 @@ pub fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize,
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -983,8 +1001,8 @@ pub fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId,
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1009,8 +1027,8 @@ pub fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1035,8 +1053,8 @@ pub fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: us
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
pub fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1068,8 +1086,8 @@ pub fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, n
}
// TODO: Generalize this for all dropdowns (also see blend_mode and channel_extration)
pub fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1095,8 +1113,8 @@ pub fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, inde
LayoutGroup::Row { widgets }.with_tooltip("Formula used to calculate the luminance of a pixel")
}
pub fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -1125,8 +1143,8 @@ pub fn boolean_operation_radio_buttons(document_node: &DocumentNode, node_id: No
LayoutGroup::Row { widgets }
}
pub fn grid_type_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn grid_type_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1150,8 +1168,8 @@ pub fn grid_type_widget(document_node: &DocumentNode, node_id: NodeId, index: us
LayoutGroup::Row { widgets }
}
pub fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1175,8 +1193,8 @@ pub fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usi
LayoutGroup::Row { widgets }
}
pub fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1200,8 +1218,8 @@ pub fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: us
LayoutGroup::Row { widgets }
}
pub fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, color_button: ColorInput, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, color_button: ColorInput, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
// Return early with just the label if the input is exposed to the graph, meaning we don't want to show the color picker widget in the Properties panel
let NodeInput::Value { tagged_value, exposed: false } = &document_node.inputs[index] else {
@ -1247,8 +1265,8 @@ pub fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
LayoutGroup::Row { widgets }
}
pub fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
pub fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, description: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, description, FrontendGraphDataType::General, blank_assist);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -1267,7 +1285,7 @@ pub fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize
}
pub fn centroid_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, "Centroid Type", FrontendGraphDataType::General, true);
let mut widgets = start_widgets(document_node, node_id, index, "Centroid Type", "TODO", FrontendGraphDataType::General, true);
let Some(input) = document_node.inputs.get(index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
return LayoutGroup::Row { widgets: vec![] };
@ -1307,13 +1325,17 @@ pub fn get_document_node<'a>(node_id: NodeId, context: &'a NodePropertiesContext
network.nodes.get(&node_id).ok_or(format!("node {node_id} not found in get_document_node"))
}
pub fn query_node_and_input_name<'a>(node_id: NodeId, input_index: usize, context: &'a NodePropertiesContext<'a>) -> Result<(&'a DocumentNode, &'a str), String> {
pub fn query_node_and_input_info<'a>(node_id: NodeId, input_index: usize, context: &'a NodePropertiesContext<'a>) -> Result<(&'a DocumentNode, &'a str, &'a str), String> {
let document_node = get_document_node(node_id, context)?;
let input_name = context
.network_interface
.input_name(&node_id, input_index, context.selection_network_path)
.ok_or("input name not found in noise_properties_scale")?;
Ok((document_node, input_name))
.ok_or("input name not found in query_node_and_input_info")?;
let input_description = context
.network_interface
.input_description(&node_id, input_index, context.selection_network_path)
.ok_or("input description not found in query_node_and_input_info")?;
Ok((document_node, input_name, input_description))
}
pub fn query_noise_pattern_state(node_id: NodeId, context: &NodePropertiesContext) -> Result<(bool, bool, bool, bool, bool, bool), String> {
@ -1369,7 +1391,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
// Monochrome
let monochrome_index = 1;
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", CheckboxInput::default(), true);
let monochrome = bool_widget(document_node, node_id, monochrome_index, "Monochrome", "TODO", CheckboxInput::default(), true);
let is_monochrome = match document_node.inputs[monochrome_index].as_value() {
Some(TaggedValue::Bool(monochrome_choice)) => *monochrome_choice,
_ => false,
@ -1422,6 +1444,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
node_id,
r.0,
r.1,
"TODO",
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(r.2)).unit("%"),
true,
);
@ -1430,6 +1453,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
node_id,
g.0,
g.1,
"TODO",
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(g.2)).unit("%"),
true,
);
@ -1438,6 +1462,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
node_id,
b.0,
b.1,
"TODO",
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(b.2)).unit("%"),
true,
);
@ -1446,6 +1471,7 @@ pub(crate) fn channel_mixer_properties(node_id: NodeId, context: &mut NodeProper
node_id,
c.0,
c.1,
"TODO",
NumberInput::default().mode_range().min(-200.).max(200.).value(Some(c.2)).unit("%"),
true,
);
@ -1522,14 +1548,14 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp
SelectiveColorChoice::Neutrals => ((30, "(Neutrals) Cyan"), (31, "(Neutrals) Magenta"), (32, "(Neutrals) Yellow"), (33, "(Neutrals) Black")),
SelectiveColorChoice::Blacks => ((34, "(Blacks) Cyan"), (35, "(Blacks) Magenta"), (36, "(Blacks) Yellow"), (37, "(Blacks) Black")),
};
let cyan = number_widget(document_node, node_id, c.0, c.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let magenta = number_widget(document_node, node_id, m.0, m.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let yellow = number_widget(document_node, node_id, y.0, y.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let black = number_widget(document_node, node_id, k.0, k.1, NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let cyan = number_widget(document_node, node_id, c.0, c.1, "TODO", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let magenta = number_widget(document_node, node_id, m.0, m.1, "TODO", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let yellow = number_widget(document_node, node_id, y.0, y.1, "TODO", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
let black = number_widget(document_node, node_id, k.0, k.1, "TODO", NumberInput::default().mode_range().min(-100.).max(100.).unit("%"), true);
// Mode
let mode_index = 1;
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", FrontendGraphDataType::General, true);
let mut mode = start_widgets(document_node, node_id, mode_index, "Mode", "TODO", FrontendGraphDataType::General, true);
mode.push(Separator::new(SeparatorType::Unrelated).widget_holder());
let Some(input) = document_node.inputs.get(mode_index) else {
@ -1565,7 +1591,7 @@ pub(crate) fn selective_color_properties(node_id: NodeId, context: &mut NodeProp
#[cfg(feature = "gpu")]
pub(crate) fn _gpu_map_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let map = text_widget(document_node, node_id, 1, "Map", true);
let map = text_widget(document_node, node_id, 1, "Map", "TODO", true);
vec![LayoutGroup::Row { widgets: map }]
}
@ -1584,7 +1610,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
return Vec::new();
}
};
let grid_type = grid_type_widget(document_node, node_id, grid_type_index, "Grid Type", true);
let grid_type = grid_type_widget(document_node, node_id, grid_type_index, "Grid Type", "TODO", true);
let mut widgets = vec![grid_type];
@ -1595,21 +1621,21 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
if let Some(&TaggedValue::GridType(grid_type)) = grid_type_input.as_non_exposed_value() {
match grid_type {
GridType::Rectangular => {
let spacing = vec2_widget(document_node, node_id, spacing_index, "Spacing", "W", "H", " px", Some(0.), add_blank_assist);
let spacing = vec2_widget(document_node, node_id, spacing_index, "Spacing", "TODO", "W", "H", " px", Some(0.), add_blank_assist);
widgets.push(spacing);
}
GridType::Isometric => {
let spacing = LayoutGroup::Row {
widgets: number_widget(document_node, node_id, spacing_index, "Spacing", NumberInput::default().label("H").min(0.).unit(" px"), true),
widgets: number_widget(document_node, node_id, spacing_index, "Spacing", "TODO", NumberInput::default().label("H").min(0.).unit(" px"), true),
};
let angles = vec2_widget(document_node, node_id, angles_index, "Angles", "", "", "°", None, add_blank_assist);
let angles = vec2_widget(document_node, node_id, angles_index, "Angles", "TODO", "", "", "°", None, add_blank_assist);
widgets.extend([spacing, angles]);
}
}
}
let rows = number_widget(document_node, node_id, rows_index, "Rows", NumberInput::default().min(1.), true);
let columns = number_widget(document_node, node_id, columns_index, "Columns", NumberInput::default().min(1.), true);
let rows = number_widget(document_node, node_id, rows_index, "Rows", "TODO", NumberInput::default().min(1.), true);
let columns = number_widget(document_node, node_id, columns_index, "Columns", "TODO", NumberInput::default().min(1.), true);
widgets.extend([LayoutGroup::Row { widgets: rows }, LayoutGroup::Row { widgets: columns }]);
@ -1624,10 +1650,10 @@ pub(crate) fn exposure_properties(node_id: NodeId, context: &mut NodePropertiesC
return Vec::new();
}
};
let exposure = number_widget(document_node, node_id, 1, "Exposure", NumberInput::default().min(-20.).max(20.), true);
let offset = number_widget(document_node, node_id, 2, "Offset", NumberInput::default().min(-0.5).max(0.5), true);
let exposure = number_widget(document_node, node_id, 1, "Exposure", "TODO", NumberInput::default().min(-20.).max(20.), true);
let offset = number_widget(document_node, node_id, 2, "Offset", "TODO", NumberInput::default().min(-0.5).max(0.5), true);
let gamma_input = NumberInput::default().min(0.01).max(9.99).increment_step(0.1);
let gamma_correction = number_widget(document_node, node_id, 3, "Gamma Correction", gamma_input, true);
let gamma_correction = number_widget(document_node, node_id, 3, "Gamma Correction", "TODO", gamma_input, true);
vec![
LayoutGroup::Row { widgets: exposure },
@ -1651,13 +1677,13 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
let clamped_index = 5;
// Size X
let size_x = number_widget(document_node, node_id, size_x_index, "Size X", NumberInput::default(), true);
let size_x = number_widget(document_node, node_id, size_x_index, "Size X", "TODO", NumberInput::default(), true);
// Size Y
let size_y = number_widget(document_node, node_id, size_y_index, "Size Y", NumberInput::default(), true);
let size_y = number_widget(document_node, node_id, size_y_index, "Size Y", "TODO", NumberInput::default(), true);
// Corner Radius
let mut corner_radius_row_1 = start_widgets(document_node, node_id, corner_radius_index, "Corner Radius", FrontendGraphDataType::Number, true);
let mut corner_radius_row_1 = start_widgets(document_node, node_id, corner_radius_index, "Corner Radius", "TODO", FrontendGraphDataType::Number, true);
corner_radius_row_1.push(Separator::new(SeparatorType::Unrelated).widget_holder());
let mut corner_radius_row_2 = vec![Separator::new(SeparatorType::Unrelated).widget_holder()];
@ -1757,7 +1783,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
}
// Clamped
let clamped = bool_widget(document_node, node_id, clamped_index, "Clamped", CheckboxInput::default(), true);
let clamped = bool_widget(document_node, node_id, clamped_index, "Clamped", "TODO", CheckboxInput::default(), true);
vec![
LayoutGroup::Row { widgets: size_x },
@ -2317,11 +2343,14 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
break 'early_return default.clone();
}
}
let Some(implementations) = &interpreted_executor::node_registry::NODE_REGISTRY.get(proto_node_identifier) else {
log::error!("Could not get implementation for protonode {proto_node_identifier:?}");
return Vec::new();
};
let proto_node_identifier = proto_node_identifier.clone();
let mut input_types = implementations
.keys()
.filter_map(|item| item.inputs.get(input_index))
@ -2329,10 +2358,12 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
.collect::<Vec<_>>();
input_types.sort_by_key(|ty| ty.type_name());
let input_type = input_types.first().cloned();
let Some(input_type) = input_type else {
log::error!("Could not get input type for protonode {proto_node_identifier:?} at index {input_index:?}");
return Vec::new();
};
input_type.clone()
}
_ => context.network_interface.input_type(&InputConnector::node(node_id, input_index), context.selection_network_path).0,
@ -2340,6 +2371,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
property_from_type(node_id, input_index, &input_type, number_options, context).unwrap_or_else(|value| value)
});
layout.extend(row);
}
}
@ -2352,7 +2384,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
.reference(&node_id, context.selection_network_path)
.cloned()
.unwrap_or_default() // If there is an error getting the reference, default to empty string
.or_else(||{
.or_else(|| {
// If there is no reference, try to get the proto node name
context.network_interface.implementation(&node_id, context.selection_network_path).and_then(|implementation|{
if let DocumentNodeImplementation::ProtoNode(protonode) = implementation {
@ -2363,10 +2395,12 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
})
})
.unwrap_or("Custom Node".to_string());
let description = context.network_interface.description(&node_id, context.selection_network_path);
let visible = context.network_interface.is_visible(&node_id, context.selection_network_path);
let pinned = context.network_interface.is_pinned(&node_id, context.selection_network_path);
LayoutGroup::Section {
name,
description,
visible,
pinned,
id: node_id.0,
@ -2387,7 +2421,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
let backup_color_index = 2;
let backup_gradient_index = 3;
let mut widgets_first_row = start_widgets(document_node, node_id, fill_index, "Fill", FrontendGraphDataType::General, true);
let mut widgets_first_row = start_widgets(document_node, node_id, fill_index, "Fill", "TODO", FrontendGraphDataType::General, true);
let (fill, backup_color, backup_gradient) = if let (Some(TaggedValue::Fill(fill)), &Some(&TaggedValue::OptionalColor(backup_color)), Some(TaggedValue::Gradient(backup_gradient))) = (
&document_node.inputs[fill_index].as_value(),
@ -2573,24 +2607,24 @@ pub fn stroke_properties(node_id: NodeId, context: &mut NodePropertiesContext) -
let line_join_index = 6;
let miter_limit_index = 7;
let color = color_widget(document_node, node_id, color_index, "Color", ColorInput::default(), true);
let weight = number_widget(document_node, node_id, weight_index, "Weight", NumberInput::default().unit(" px").min(0.), true);
let color = color_widget(document_node, node_id, color_index, "Color", "TODO", ColorInput::default(), true);
let weight = number_widget(document_node, node_id, weight_index, "Weight", "TODO", NumberInput::default().unit(" px").min(0.), true);
let dash_lengths_val = match &document_node.inputs[dash_lengths_index].as_value() {
Some(TaggedValue::VecF64(x)) => x,
_ => &vec![],
};
let dash_lengths = vec_f64_input(document_node, node_id, dash_lengths_index, "Dash Lengths", TextInput::default().centered(true), true);
let dash_lengths = vec_f64_input(document_node, node_id, dash_lengths_index, "Dash Lengths", "TODO", TextInput::default().centered(true), true);
let number_input = NumberInput::default().unit(" px").disabled(dash_lengths_val.is_empty());
let dash_offset = number_widget(document_node, node_id, dash_offset_index, "Dash Offset", number_input, true);
let line_cap = line_cap_widget(document_node, node_id, line_cap_index, "Line Cap", true);
let line_join = line_join_widget(document_node, node_id, line_join_index, "Line Join", true);
let dash_offset = number_widget(document_node, node_id, dash_offset_index, "Dash Offset", "TODO", number_input, true);
let line_cap = line_cap_widget(document_node, node_id, line_cap_index, "Line Cap", "TODO", true);
let line_join = line_join_widget(document_node, node_id, line_join_index, "Line Join", "TODO", true);
let line_join_val = match &document_node.inputs[line_join_index].as_value() {
Some(TaggedValue::LineJoin(x)) => x,
_ => &LineJoin::Miter,
};
let number_input = NumberInput::default().min(0.).disabled(line_join_val != &LineJoin::Miter);
let miter_limit = number_widget(document_node, node_id, miter_limit_index, "Miter Limit", number_input, true);
let miter_limit = number_widget(document_node, node_id, miter_limit_index, "Miter Limit", "TODO", number_input, true);
vec![
color,
@ -2616,16 +2650,16 @@ pub fn offset_path_properties(node_id: NodeId, context: &mut NodePropertiesConte
let miter_limit_index = 3;
let number_input = NumberInput::default().unit(" px");
let distance = number_widget(document_node, node_id, distance_index, "Offset", number_input, true);
let distance = number_widget(document_node, node_id, distance_index, "Offset", "TODO", number_input, true);
let line_join = line_join_widget(document_node, node_id, line_join_index, "Line Join", true);
let line_join = line_join_widget(document_node, node_id, line_join_index, "Line Join", "TODO", true);
let line_join_val = match &document_node.inputs[line_join_index].as_value() {
Some(TaggedValue::LineJoin(x)) => x,
_ => &LineJoin::Miter,
};
let number_input = NumberInput::default().min(0.).disabled(line_join_val != &LineJoin::Miter);
let miter_limit = number_widget(document_node, node_id, miter_limit_index, "Miter Limit", number_input, true);
let miter_limit = number_widget(document_node, node_id, miter_limit_index, "Miter Limit", "TODO", number_input, true);
vec![LayoutGroup::Row { widgets: distance }, line_join, LayoutGroup::Row { widgets: miter_limit }]
}
@ -2643,7 +2677,7 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
let operation_b_index = 2;
let expression = (|| {
let mut widgets = start_widgets(document_node, node_id, expression_index, "Expression", FrontendGraphDataType::General, true);
let mut widgets = start_widgets(document_node, node_id, expression_index, "Expression", "TODO", FrontendGraphDataType::General, true);
let Some(input) = document_node.inputs.get(expression_index) else {
log::warn!("A widget failed to be built because its node's input index is invalid.");
@ -2677,7 +2711,7 @@ pub fn math_properties(node_id: NodeId, context: &mut NodePropertiesContext) ->
}
widgets
})();
let operand_b = number_widget(document_node, node_id, operation_b_index, "Operand B", NumberInput::default(), true);
let operand_b = number_widget(document_node, node_id, operation_b_index, "Operand B", "TODO", NumberInput::default(), true);
let operand_a_hint = vec![TextLabel::new("(Operand A is the primary input)").widget_holder()];
vec![

View file

@ -48,6 +48,7 @@ pub struct FrontendGraphInput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
pub description: String,
#[serde(rename = "resolvedType")]
pub resolved_type: Option<String>,
#[serde(rename = "validTypes")]
@ -61,6 +62,7 @@ pub struct FrontendGraphOutput {
#[serde(rename = "dataType")]
pub data_type: FrontendGraphDataType,
pub name: String,
pub description: String,
#[serde(rename = "resolvedType")]
pub resolved_type: Option<String>,
#[serde(rename = "connectedTo")]

View file

@ -818,6 +818,7 @@ impl NodeNetworkInterface {
FrontendGraphOutput {
data_type,
name: import_name,
description: String::new(),
resolved_type: Some(format!("{input_type:?} from {type_source:?}")),
connected_to,
},
@ -902,6 +903,7 @@ impl NodeNetworkInterface {
FrontendGraphInput {
data_type: frontend_data_type,
name: export_name,
description: String::new(),
resolved_type: input_type.map(|(export_type, source)| format!("{export_type:?} from {source:?}")),
valid_types: self.valid_input_types(&InputConnector::Export(*export_index), network_path).iter().map(|ty| ty.to_string()).collect(),
connected_to,
@ -1132,6 +1134,14 @@ impl NodeNetworkInterface {
value.as_str()
}
pub fn input_description(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&str> {
let Some(value) = self.input_metadata(node_id, index, "input_description", network_path) else {
log::error!("Could not get input_description for node {node_id} index {index}");
return None;
};
value.as_str()
}
pub fn input_properties_row(&self, node_id: &NodeId, index: usize, network_path: &[NodeId]) -> Option<&PropertiesRow> {
self.node_metadata(node_id, network_path)
.and_then(|node_metadata| node_metadata.persistent_metadata.input_properties.get(index))
@ -1145,16 +1155,7 @@ impl NodeNetworkInterface {
input_row.input_data.get(field)
}
// Use frontend display name instead
fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get node_metadata in display_name");
return "".to_string();
};
node_metadata.persistent_metadata.display_name.clone()
}
pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
let is_layer = self
.node_metadata(node_id, network_path)
.expect("Could not get persistent node metadata in untitled_layer_label")
@ -1165,17 +1166,30 @@ impl NodeNetworkInterface {
return "".to_string();
};
if self.display_name(node_id, network_path).is_empty() {
let display_name = if let Some(node_metadata) = self.node_metadata(node_id, network_path) {
node_metadata.persistent_metadata.display_name.clone()
} else {
log::error!("Could not get node_metadata in display_name");
String::new()
};
if display_name.is_empty() {
if is_layer && *reference == Some("Merge".to_string()) {
"Untitled Layer".to_string()
} else {
reference.clone().unwrap_or("Untitled Node".to_string())
}
} else {
self.display_name(node_id, network_path)
display_name
}
}
pub fn description(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
self.get_node_definition(network_path, *node_id)
.and_then(|node_definition| Some(node_definition.description.to_string()))
.unwrap_or_default()
}
pub fn is_locked(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else {
log::error!("Could not get persistent node metadata in is_locked for node {node_id}");
@ -1472,7 +1486,7 @@ impl NodeNetworkInterface {
_ => {}
};
let name = self.frontend_display_name(node_id, network_path);
let name = self.display_name(node_id, network_path);
div.set_text_content(Some(&name));
@ -3415,9 +3429,9 @@ impl NodeNetworkInterface {
return;
};
if insert_index == -1 {
node_metadata.persistent_metadata.input_properties.push(input_name.into());
node_metadata.persistent_metadata.input_properties.push((input_name, "TODO").into());
} else {
node_metadata.persistent_metadata.input_properties.insert(insert_index as usize, input_name.into());
node_metadata.persistent_metadata.input_properties.insert(insert_index as usize, (input_name, "TODO").into());
}
// Clear the reference to the nodes definition
@ -6159,7 +6173,7 @@ pub enum WidgetOverride {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct PropertiesRow {
/// A general datastore than can store key value pairs of any types for any input
// TODO: This could be simplified to just Value, and key value pairs could be stored as the Object variant
// TODO: This could be simplified to just Value, and key value pairs could be stored as the Value::Object variant
pub input_data: HashMap<String, Value>,
// An input can override a widget, which would otherwise be automatically generated from the type
// The string is the identifier to the widget override function stored in INPUT_OVERRIDES
@ -6168,20 +6182,21 @@ pub struct PropertiesRow {
impl Default for PropertiesRow {
fn default() -> Self {
"".into()
("", "TODO").into()
}
}
impl From<&str> for PropertiesRow {
fn from(input_name: &str) -> Self {
PropertiesRow::with_override(input_name, WidgetOverride::None)
impl From<(&str, &str)> for PropertiesRow {
fn from(input_name_and_description: (&str, &str)) -> Self {
PropertiesRow::with_override(input_name_and_description.0, input_name_and_description.1, WidgetOverride::None)
}
}
impl PropertiesRow {
pub fn with_override(input_name: &str, widget_override: WidgetOverride) -> Self {
pub fn with_override(input_name: &str, input_description: &str, widget_override: WidgetOverride) -> Self {
let mut input_data = HashMap::new();
input_data.insert("input_name".to_string(), Value::String(input_name.to_string()));
input_data.insert("input_description".to_string(), Value::String(input_description.to_string()));
match widget_override {
WidgetOverride::None => PropertiesRow { input_data, widget_override: None },
WidgetOverride::Hidden => PropertiesRow {
@ -6351,7 +6366,7 @@ impl From<DocumentNodePersistentMetadataInputNames> for DocumentNodePersistentMe
.as_ref()
.and_then(|reference| resolve_document_node_type(reference))
.map(|definition| definition.node_template.persistent_node_metadata.input_properties.clone())
.unwrap_or(old.input_names.into_iter().map(|name| name.as_str().into()).collect());
.unwrap_or(old.input_names.into_iter().map(|name| (name.as_str(), "TODO").into()).collect());
DocumentNodePersistentMetadata {
reference: old.reference,

View file

@ -830,7 +830,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
// Upgrade artboard name being passed as hidden value input to "To Artboard"
if reference == "Artboard" && upgrade_from_before_returning_nested_click_targets {
let label = document.network_interface.frontend_display_name(node_id, network_path);
let label = document.network_interface.display_name(node_id, network_path);
document
.network_interface
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);

View file

@ -1069,7 +1069,7 @@
<div class="secondary" class:in-selected-network={$nodeGraph.inSelectedNetwork}>
{#each exposedInputsOutputs as [input, output]}
<div class={`secondary-row expanded ${input !== undefined ? "input" : "output"}`}>
<TextLabel tooltip={input !== undefined ? input.name : output.name}>
<TextLabel tooltip={(input !== undefined ? `${input.name}\n\n${input.description}` : `${output.name}\n\n${output.description}`).trim()}>
{input !== undefined ? input.name : output.name}
</TextLabel>
</div>

View file

@ -26,7 +26,7 @@
<LayoutCol class={`widget-section ${className}`.trim()} {classes}>
<button class="header" class:expanded on:click|stopPropagation={() => (expanded = !expanded)} tabindex="0">
<div class="expand-arrow" />
<TextLabel bold={true}>{widgetData.name}</TextLabel>
<TextLabel tooltip={widgetData.description} bold={true}>{widgetData.name}</TextLabel>
<IconButton
icon={widgetData.pinned ? "PinActive" : "PinInactive"}
tooltip={widgetData.pinned ? "Unpin this node so it's no longer shown here when nothing is selected" : "Pin this node so it's shown here when nothing is selected"}

View file

@ -110,12 +110,9 @@ export class UpdateNodeGraphTransform extends JsMessage {
readonly transform!: NodeGraphTransform;
}
const InputTypeDescriptions = Transform(({ obj }) => new Map(obj.inputTypeDescriptions));
const NodeDescriptions = Transform(({ obj }) => new Map(obj.nodeDescriptions));
export class SendUIMetadata extends JsMessage {
@InputTypeDescriptions
readonly inputTypeDescriptions!: Map<string, string>;
@NodeDescriptions
readonly nodeDescriptions!: Map<string, string>;
@Type(() => FrontendNode)
@ -220,6 +217,8 @@ export class FrontendGraphInput {
readonly name!: string;
readonly description!: string;
readonly resolvedType!: string | undefined;
readonly validTypes!: string[];
@ -251,6 +250,8 @@ export class FrontendGraphOutput {
readonly name!: string;
readonly description!: string;
readonly resolvedType!: string | undefined;
@CreateInputConnectorArray
@ -1508,7 +1509,7 @@ export function isWidgetTable(layoutTable: LayoutGroup): layoutTable is WidgetTa
return Boolean((layoutTable as WidgetTable)?.tableWidgets);
}
export type WidgetSection = { name: string; visible: boolean; pinned: boolean; id: bigint; layout: LayoutGroup[] };
export type WidgetSection = { name: string; description: string; visible: boolean; pinned: boolean; id: bigint; layout: LayoutGroup[] };
export function isWidgetSection(layoutRow: LayoutGroup): layoutRow is WidgetSection {
return Boolean((layoutRow as WidgetSection)?.layout);
}
@ -1550,6 +1551,7 @@ function createLayoutGroup(layoutGroup: any): LayoutGroup {
if (layoutGroup.section) {
const result: WidgetSection = {
name: layoutGroup.section.name,
description: layoutGroup.section.description,
visible: layoutGroup.section.visible,
pinned: layoutGroup.section.pinned,
id: layoutGroup.section.id,

View file

@ -43,7 +43,6 @@ export function createNodeGraphState(editor: Editor) {
wires: [] as FrontendNodeWire[],
wiresDirectNotGridAligned: false,
wirePathInProgress: undefined as WirePath | undefined,
inputTypeDescriptions: new Map<string, string>(),
nodeDescriptions: new Map<string, string>(),
nodeTypes: [] as FrontendNodeType[],
thumbnails: new Map<bigint, string>(),
@ -55,11 +54,10 @@ export function createNodeGraphState(editor: Editor) {
});
// Set up message subscriptions on creation
editor.subscriptions.subscribeJsMessage(SendUIMetadata, (UIMetadata) => {
editor.subscriptions.subscribeJsMessage(SendUIMetadata, (uiMetadata) => {
update((state) => {
state.inputTypeDescriptions = UIMetadata.inputTypeDescriptions;
state.nodeDescriptions = UIMetadata.nodeDescriptions;
state.nodeTypes = UIMetadata.nodeTypes;
state.nodeDescriptions = uiMetadata.nodeDescriptions;
state.nodeTypes = uiMetadata.nodeTypes;
return state;
});
});