mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-24 08:05:04 +00:00
Parse description from node doc comments (#2089)
* Parse description from node doc comments * Add node description tooltips * Code review --------- Co-authored-by: Adam G <adamgerhant@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
8fdecaa487
commit
35f7cfac80
12 changed files with 144 additions and 24 deletions
|
@ -1,5 +1,6 @@
|
|||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||
use crate::messages::dialog::DialogMessageData;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graphene_core::text::Font;
|
||||
|
@ -137,6 +138,13 @@ impl Dispatcher {
|
|||
// Load the default font
|
||||
let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.into(), graphene_core::consts::DEFAULT_FONT_STYLE.into());
|
||||
queue.add(FrontendMessage::TriggerFontLoad { font, is_default: true });
|
||||
|
||||
// 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(),
|
||||
});
|
||||
}
|
||||
Message::Batched(messages) => {
|
||||
messages.iter().for_each(|message| self.handle_message(message.to_owned()));
|
||||
|
|
|
@ -39,6 +39,16 @@ pub enum FrontendMessage {
|
|||
},
|
||||
DisplayRemoveEditableTextbox,
|
||||
|
||||
// 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")]
|
||||
node_types: Vec<FrontendNodeType>,
|
||||
},
|
||||
|
||||
// Trigger prefix: cause a browser API to do something
|
||||
TriggerAboutGraphiteLocalizedCommitDate {
|
||||
#[serde(rename = "commitDate")]
|
||||
|
@ -245,10 +255,6 @@ pub enum FrontendMessage {
|
|||
id: NodeId,
|
||||
value: String,
|
||||
},
|
||||
UpdateNodeTypes {
|
||||
#[serde(rename = "nodeTypes")]
|
||||
node_types: Vec<FrontendNodeType>,
|
||||
},
|
||||
UpdateOpenDocumentsList {
|
||||
#[serde(rename = "openDocuments")]
|
||||
open_documents: Vec<FrontendDocumentDetails>,
|
||||
|
|
|
@ -50,6 +50,9 @@ pub struct DocumentNodeDefinition {
|
|||
/// Definition specific data. In order for the editor to access this data, the reference will be used.
|
||||
pub category: &'static str,
|
||||
pub properties: &'static (dyn Fn(&DocumentNode, NodeId, &mut NodePropertiesContext) -> Vec<LayoutGroup> + Sync),
|
||||
|
||||
/// User-facing description of the node's functionality.
|
||||
pub description: Cow<'static, str>,
|
||||
}
|
||||
|
||||
// We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time.
|
||||
|
@ -77,6 +80,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("The identity node simply passes its data through. You can use this to organize your node graph if you want."),
|
||||
properties: &|_document_node, _node_id, _context| node_properties::string_properties("The identity node simply passes its data through"),
|
||||
},
|
||||
// TODO: Auto-generate this from its proto node macro
|
||||
|
@ -97,6 +101,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("The Monitor node is used by the editor to access the data flowing through it"),
|
||||
properties: &|_document_node, _node_id, _context| node_properties::string_properties("The Monitor node is used by the editor to access the data flowing through it"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -203,6 +208,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("The Merge node combines graphical data through composition"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -313,6 +319,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Creates a new Artboard which can be used as a working surface"),
|
||||
properties: &node_properties::artboard_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -393,6 +400,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Loads an image from a given url."),
|
||||
properties: &node_properties::load_image_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -457,6 +465,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Creates a new canvas object."),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -549,6 +558,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Draws raster data to a canvas element."),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -640,6 +650,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Rasterizes the given vector data"),
|
||||
properties: &node_properties::rasterize_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -708,6 +719,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Creates an embedded image with the given transform"),
|
||||
properties: &|_document_node, _node_id, _context| node_properties::string_properties("Creates an embedded image with the given transform"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -786,6 +798,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("Generates different noise patters"),
|
||||
properties: &node_properties::noise_pattern_properties,
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
|
@ -808,6 +821,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::mask_properties,
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
|
@ -831,6 +845,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::insert_channel_properties,
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
|
@ -855,6 +870,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -968,6 +984,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1036,6 +1053,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1054,6 +1072,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1072,6 +1091,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1120,6 +1140,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &|_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1200,6 +1221,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1278,6 +1300,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1356,6 +1379,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1444,6 +1468,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1468,6 +1493,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::node_no_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1546,6 +1572,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1625,6 +1652,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1690,6 +1718,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1760,6 +1789,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1840,6 +1870,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
#[cfg(feature = "gpu")]
|
||||
|
@ -1861,6 +1892,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1878,6 +1910,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1903,6 +1936,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::brightness_contrast_properties,
|
||||
},
|
||||
// Aims for interoperable compatibility with:
|
||||
|
@ -1926,6 +1960,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::curves_properties,
|
||||
},
|
||||
(*IMAGINATE_NODE).clone(),
|
||||
|
@ -1949,6 +1984,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::line_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -1971,6 +2007,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::spline_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -2041,6 +2078,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::node_no_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -2076,6 +2114,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
..Default::default()
|
||||
},
|
||||
},
|
||||
description: Cow::Borrowed("TODO"),
|
||||
properties: &node_properties::text_properties,
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
|
@ -2163,6 +2202,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::transform_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Boolean Operation",
|
||||
|
@ -2232,6 +2272,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::boolean_operation_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Copy to Points",
|
||||
|
@ -2269,6 +2310,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::copy_to_points_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Sample Points",
|
||||
|
@ -2365,6 +2407,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::sample_points_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
DocumentNodeDefinition {
|
||||
identifier: "Scatter Points",
|
||||
|
@ -2437,6 +2480,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::poisson_disk_points_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
// TODO: This needs to work with resolution-aware (raster with footprint, post-Cull node) data.
|
||||
DocumentNodeDefinition {
|
||||
|
@ -2455,6 +2499,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
properties: &node_properties::index_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -2505,7 +2550,12 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
}
|
||||
}
|
||||
|
||||
let NodeMetadata { display_name, category, fields } = metadata;
|
||||
let NodeMetadata {
|
||||
display_name,
|
||||
category,
|
||||
fields,
|
||||
description,
|
||||
} = metadata;
|
||||
let Some(implementations) = &node_registry.get(&id) else { continue };
|
||||
let valid_inputs: HashSet<_> = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect();
|
||||
let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
|
||||
|
@ -2582,6 +2632,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
},
|
||||
},
|
||||
category: category.unwrap_or("UNCATEGORIZED"),
|
||||
description: Cow::Borrowed(description),
|
||||
properties,
|
||||
};
|
||||
custom.push(node);
|
||||
|
@ -2708,6 +2759,7 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
|
|||
},
|
||||
},
|
||||
properties: &node_properties::imaginate_properties,
|
||||
description: Cow::Borrowed("TODO"),
|
||||
});
|
||||
|
||||
pub fn resolve_document_node_type(identifier: &str) -> Option<&DocumentNodeDefinition> {
|
||||
|
@ -2722,6 +2774,13 @@ pub fn collect_node_types() -> Vec<FrontendNodeType> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn collect_node_descriptions() -> Vec<(String, String)> {
|
||||
DOCUMENT_NODE_TYPES
|
||||
.iter()
|
||||
.map(|definition| (definition.identifier.to_string(), definition.description.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl DocumentNodeDefinition {
|
||||
/// Converts the [DocumentNodeDefinition] type to a [NodeTemplate], using the provided `input_override` and falling back to the default inputs.
|
||||
/// `input_override` does not have to be the correct length.
|
||||
|
|
|
@ -1358,9 +1358,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
responses.add(BroadcastEvent::SelectionChanged);
|
||||
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
|
||||
let node_types = document_node_definitions::collect_node_types();
|
||||
responses.add(FrontendMessage::UpdateNodeTypes { node_types });
|
||||
}
|
||||
NodeGraphMessage::UpdateTypes { resolved_types, node_graph_errors } => {
|
||||
for (path, node_type) in resolved_types.add {
|
||||
|
@ -1897,7 +1894,7 @@ impl NodeGraphMessageHandler {
|
|||
.node_metadata(&node_id, breadcrumb_network_path)
|
||||
.is_some_and(|node_metadata| node_metadata.persistent_metadata.is_layer()),
|
||||
can_be_layer: can_be_layer_lookup.contains(&node_id),
|
||||
reference: None,
|
||||
reference: network_interface.reference(&node_id, breadcrumb_network_path),
|
||||
display_name: network_interface.frontend_display_name(&node_id, breadcrumb_network_path),
|
||||
primary_input,
|
||||
exposed_inputs,
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
<TextLabel>{nodeCategory[0]}</TextLabel>
|
||||
</summary>
|
||||
{#each nodeCategory[1].nodes as nodeType}
|
||||
<TextButton {disabled} label={nodeType.name} action={() => dispatch("selectNodeType", nodeType.name)} />
|
||||
<TextButton {disabled} label={nodeType.name} tooltip={$nodeGraph.nodeDescriptions.get(nodeType.name)} action={() => dispatch("selectNodeType", nodeType.name)} />
|
||||
{/each}
|
||||
</details>
|
||||
{:else}
|
||||
|
|
|
@ -461,6 +461,7 @@
|
|||
{@const stackDataInput = node.exposedInputs[0]}
|
||||
{@const layerAreaWidth = $nodeGraph.layerWidths.get(node.id) || 8}
|
||||
{@const layerChainWidth = $nodeGraph.chainWidths.get(node.id) || 0}
|
||||
{@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
|
||||
<div
|
||||
class="layer"
|
||||
class:selected={$nodeGraph.selected.includes(node.id)}
|
||||
|
@ -474,6 +475,7 @@
|
|||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||
style:--layer-area-width={layerAreaWidth}
|
||||
style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0}
|
||||
title={description + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${node.id}` : "")}
|
||||
data-node={node.id}
|
||||
bind:this={nodeElements[nodeIndex]}
|
||||
>
|
||||
|
@ -556,9 +558,7 @@
|
|||
{/if}
|
||||
<div class="details">
|
||||
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
|
||||
<span title={editor.handle.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined}>
|
||||
{node.displayName}
|
||||
</span>
|
||||
<span>{node.displayName}</span>
|
||||
</div>
|
||||
<div class="solo-drag-grip" title="Drag only this layer without pushing others outside the stack"></div>
|
||||
<IconButton
|
||||
|
@ -604,6 +604,7 @@
|
|||
{#each Array.from($nodeGraph.nodes.values()).flatMap((node, nodeIndex) => (node.isLayer ? [] : [{ node, nodeIndex }])) as { node, nodeIndex } (nodeIndex)}
|
||||
{@const exposedInputsOutputs = [...node.exposedInputs, ...node.exposedOutputs]}
|
||||
{@const clipPathId = String(Math.random()).substring(2)}
|
||||
{@const description = (node.reference && $nodeGraph.nodeDescriptions.get(node.reference)) || undefined}
|
||||
<div
|
||||
class="node"
|
||||
class:selected={$nodeGraph.selected.includes(node.id)}
|
||||
|
@ -614,6 +615,7 @@
|
|||
style:--clip-path-id={`url(#${clipPathId})`}
|
||||
style:--data-color={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()})`}
|
||||
style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`}
|
||||
title={description + (editor.handle.inDevelopmentMode() ? `\n\nNode ID: ${node.id}` : "")}
|
||||
data-node={node.id}
|
||||
bind:this={nodeElements[nodeIndex]}
|
||||
>
|
||||
|
@ -625,7 +627,7 @@
|
|||
<div class="primary" class:in-selected-network={$nodeGraph.inSelectedNetwork} class:no-secondary-section={exposedInputsOutputs.length === 0}>
|
||||
<IconLabel icon={nodeIcon(node.reference)} />
|
||||
<!-- TODO: Allow the user to edit the name, just like in the Layers panel -->
|
||||
<TextLabel tooltip={editor.handle.inDevelopmentMode() ? `Node ID: ${node.id}` : undefined}>{node.displayName}</TextLabel>
|
||||
<TextLabel>{node.displayName}</TextLabel>
|
||||
</div>
|
||||
<!-- Secondary rows -->
|
||||
{#if exposedInputsOutputs.length > 0}
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
type FrontendNodeWire as FrontendNodeWire,
|
||||
type FrontendNodeType,
|
||||
type WirePath,
|
||||
SendUIMetadata,
|
||||
UpdateBox,
|
||||
UpdateClickTargets,
|
||||
UpdateContextMenuInformation,
|
||||
|
@ -19,7 +20,6 @@ import {
|
|||
UpdateNodeGraph,
|
||||
UpdateNodeGraphSelection,
|
||||
UpdateNodeGraphTransform,
|
||||
UpdateNodeTypes,
|
||||
UpdateNodeThumbnail,
|
||||
UpdateWirePathInProgress,
|
||||
UpdateZoomWithScroll,
|
||||
|
@ -38,6 +38,8 @@ export function createNodeGraphState(editor: Editor) {
|
|||
nodes: new Map<bigint, FrontendNode>(),
|
||||
wires: [] as FrontendNodeWire[],
|
||||
wirePathInProgress: undefined as WirePath | undefined,
|
||||
inputTypeDescriptions: new Map<string, string>(),
|
||||
nodeDescriptions: new Map<string, string>(),
|
||||
nodeTypes: [] as FrontendNodeType[],
|
||||
zoomWithScroll: false as boolean,
|
||||
thumbnails: new Map<bigint, string>(),
|
||||
|
@ -47,6 +49,14 @@ export function createNodeGraphState(editor: Editor) {
|
|||
});
|
||||
|
||||
// Set up message subscriptions on creation
|
||||
editor.subscriptions.subscribeJsMessage(SendUIMetadata, (UIMetadata) => {
|
||||
update((state) => {
|
||||
state.inputTypeDescriptions = UIMetadata.inputTypeDescriptions;
|
||||
state.nodeDescriptions = UIMetadata.nodeDescriptions;
|
||||
state.nodeTypes = UIMetadata.nodeTypes;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateBox, (updateBox) => {
|
||||
update((state) => {
|
||||
state.box = updateBox.box;
|
||||
|
@ -108,12 +118,6 @@ export function createNodeGraphState(editor: Editor) {
|
|||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateNodeTypes, (updateNodeTypes) => {
|
||||
update((state) => {
|
||||
state.nodeTypes = updateNodeTypes.nodeTypes;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateNodeThumbnail, (updateNodeThumbnail) => {
|
||||
update((state) => {
|
||||
state.thumbnails.set(updateNodeThumbnail.id, updateNodeThumbnail.value);
|
||||
|
|
|
@ -89,7 +89,14 @@ export class UpdateNodeGraphTransform extends JsMessage {
|
|||
readonly transform!: NodeGraphTransform;
|
||||
}
|
||||
|
||||
export class UpdateNodeTypes extends JsMessage {
|
||||
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)
|
||||
readonly nodeTypes!: FrontendNodeType[];
|
||||
}
|
||||
|
@ -1547,6 +1554,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
DisplayEditableTextbox,
|
||||
DisplayEditableTextboxTransform,
|
||||
DisplayRemoveEditableTextbox,
|
||||
SendUIMetadata,
|
||||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerCopyToClipboardBlobUrl,
|
||||
TriggerDelayedZoomCanvasToFitAll,
|
||||
|
@ -1596,7 +1604,6 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdateNodeGraphSelection,
|
||||
UpdateNodeGraphTransform,
|
||||
UpdateNodeThumbnail,
|
||||
UpdateNodeTypes,
|
||||
UpdateOpenDocumentsList,
|
||||
UpdatePropertyPanelSectionsLayout,
|
||||
UpdateToolOptionsLayout,
|
||||
|
|
|
@ -34,6 +34,7 @@ pub struct NodeMetadata {
|
|||
pub display_name: &'static str,
|
||||
pub category: Option<&'static str>,
|
||||
pub fields: Vec<FieldMetadata>,
|
||||
pub description: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -500,6 +500,7 @@ fn modify_existing() {
|
|||
);
|
||||
}
|
||||
|
||||
// Do we want to enforce that all serialized/deserialized hashmaps are a vec of tuples?
|
||||
// TODO: Eventually remove this (probably starting late 2024)
|
||||
use serde::de::{SeqAccess, Visitor};
|
||||
use serde::ser::SerializeSeq;
|
||||
|
|
|
@ -22,6 +22,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||
fields,
|
||||
body,
|
||||
crate_name: graphene_core_crate,
|
||||
description,
|
||||
..
|
||||
} = parsed;
|
||||
|
||||
|
@ -257,6 +258,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
|||
let metadata = NodeMetadata {
|
||||
display_name: #display_name,
|
||||
category: #category,
|
||||
description: #description,
|
||||
fields: vec![
|
||||
#(
|
||||
FieldMetadata {
|
||||
|
|
|
@ -5,7 +5,7 @@ use quote::{format_ident, ToTokens};
|
|||
use syn::parse::{Parse, ParseStream, Parser};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::{Comma, RArrow};
|
||||
use syn::{Attribute, Error, ExprTuple, FnArg, GenericParam, Ident, ItemFn, LitFloat, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, Type, WhereClause};
|
||||
use syn::{AttrStyle, Attribute, Error, Expr, ExprTuple, FnArg, GenericParam, Ident, ItemFn, Lit, LitFloat, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, Type, WhereClause};
|
||||
|
||||
use crate::codegen::generate_node_code;
|
||||
|
||||
|
@ -30,6 +30,7 @@ pub(crate) struct ParsedNodeFn {
|
|||
pub(crate) fields: Vec<ParsedField>,
|
||||
pub(crate) body: TokenStream2,
|
||||
pub(crate) crate_name: proc_macro_crate::FoundCrate,
|
||||
pub(crate) description: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -208,6 +209,22 @@ fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNo
|
|||
format!("Failed to find location of graphene_core. Make sure it is imported as a dependency: {}", e),
|
||||
)
|
||||
})?;
|
||||
let description = input_fn
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
if a.style != AttrStyle::Outer {
|
||||
return None;
|
||||
}
|
||||
let Meta::NameValue(name_val) = &a.meta else { return None };
|
||||
if name_val.path.get_ident().map(|x| x.to_string()) != Some("doc".into()) {
|
||||
return None;
|
||||
}
|
||||
let Expr::Lit(expr_lit) = &name_val.value else { return None };
|
||||
let Lit::Str(ref text) = expr_lit.lit else { return None };
|
||||
Some(text.value().trim().to_string())
|
||||
})
|
||||
.fold(String::new(), |acc, b| acc + &b + "\n");
|
||||
|
||||
Ok(ParsedNodeFn {
|
||||
attributes,
|
||||
|
@ -222,6 +239,7 @@ fn parse_node_fn(attr: TokenStream2, item: TokenStream2) -> syn::Result<ParsedNo
|
|||
where_clause,
|
||||
body,
|
||||
crate_name,
|
||||
description,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -490,6 +508,7 @@ mod tests {
|
|||
assert_eq!(parsed.attributes.path, expected.attributes.path);
|
||||
assert_eq!(parsed.attributes.skip_impl, expected.attributes.skip_impl);
|
||||
assert_eq!(parsed.fields.len(), expected.fields.len());
|
||||
assert_eq!(parsed.description, expected.description);
|
||||
|
||||
for (parsed_field, expected_field) in parsed.fields.iter().zip(expected.fields.iter()) {
|
||||
match (parsed_field, expected_field) {
|
||||
|
@ -550,6 +569,8 @@ mod tests {
|
|||
fn test_basic_node() {
|
||||
let attr = quote!(category("Math: Arithmetic"), path(graphene_core::TestNode), skip_impl);
|
||||
let input = quote!(
|
||||
/// Multi
|
||||
/// Line
|
||||
fn add(a: f64, b: f64) -> f64 {
|
||||
a + b
|
||||
}
|
||||
|
@ -588,6 +609,7 @@ mod tests {
|
|||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::from("Multi\nLine\n"),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -597,6 +619,10 @@ mod tests {
|
|||
fn test_node_with_impl_node() {
|
||||
let attr = quote!(category("General"));
|
||||
let input = quote!(
|
||||
/**
|
||||
Hello
|
||||
World
|
||||
*/
|
||||
fn transform<T: 'static>(footprint: Footprint, transform_target: impl Node<Footprint, Output = T>, translate: DVec2) -> T {
|
||||
// Implementation details...
|
||||
}
|
||||
|
@ -644,6 +670,7 @@ mod tests {
|
|||
],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::from("Hello\n\t\t\t\tWorld\n"),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -653,6 +680,7 @@ mod tests {
|
|||
fn test_node_with_default_values() {
|
||||
let attr = quote!(category("Vector: Shape"));
|
||||
let input = quote!(
|
||||
/// Test
|
||||
fn circle(_: (), #[default(50.)] radius: f64) -> VectorData {
|
||||
// Implementation details...
|
||||
}
|
||||
|
@ -691,6 +719,7 @@ mod tests {
|
|||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: "Test\n".into(),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -743,6 +772,7 @@ mod tests {
|
|||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -796,6 +826,7 @@ mod tests {
|
|||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -843,6 +874,7 @@ mod tests {
|
|||
}],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
@ -880,6 +912,7 @@ mod tests {
|
|||
fields: vec![],
|
||||
body: TokenStream2::new(),
|
||||
crate_name: FoundCrate::Itself,
|
||||
description: String::new(),
|
||||
};
|
||||
|
||||
assert_parsed_node_fn(&parsed, &expected);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue