Storing display name as a node input

This commit is contained in:
Adam
2024-09-03 15:43:37 -07:00
parent 3738a46e48
commit 91ee7b9ff0
2 changed files with 128 additions and 38 deletions
@@ -74,12 +74,8 @@ impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandl
.all_layers() .all_layers()
.filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[])) .filter(|&layer| document.network_interface.is_artboard(&layer.to_node(), &[]))
.map(|layer| { .map(|layer| {
let name = document let display_name = document.network_interface.display_name(&layer.to_node(), &[]);
.network_interface let name = if display_name.is_empty() { "Artboard".to_string() } else { display_name };
.node_metadata(&layer.to_node(), &[])
.map(|node| node.persistent_metadata.display_name.clone())
.and_then(|name| if name.is_empty() { None } else { Some(name) })
.unwrap_or_else(|| "Artboard".to_string());
(layer, name) (layer, name)
}) })
.collect(); .collect();
@@ -23,6 +23,7 @@ pub struct NodeNetworkInterface {
/// The node graph that generates this document's artwork. It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content. /// The node graph that generates this document's artwork. It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content.
/// A public mutable reference should never be created. It should only be mutated through custom setters which perform the necessary side effects to keep network_metadata in sync /// A public mutable reference should never be created. It should only be mutated through custom setters which perform the necessary side effects to keep network_metadata in sync
network: NodeNetwork, network: NodeNetwork,
// TODO: Remove and store in node network with IDs based on Self::metadata_node_id
/// Stores all editor information for a NodeNetwork. Should automatically kept in sync by the setter methods when changes to the document network are made. /// Stores all editor information for a NodeNetwork. Should automatically kept in sync by the setter methods when changes to the document network are made.
network_metadata: NodeNetworkMetadata, network_metadata: NodeNetworkMetadata,
// TODO: Wrap in TransientMetadata Option // TODO: Wrap in TransientMetadata Option
@@ -37,6 +38,36 @@ pub struct NodeNetworkInterface {
transaction_status: TransactionStatus, transaction_status: TransactionStatus,
} }
#[derive(Hash)]
pub enum Metadata {
// Network persistent metadata
NodeMetadata,
Previewing,
NavigationMetadata,
SelectionUndoHistory,
SelectionRedoHistory,
// Network transient metadata
SelectedNodes,
StackDependents,
AllNodesBoundingBox,
OutwardWires,
ImportExportPorts,
// Node persistent metadata
Reference,
DisplayName,
InputNames,
OutputNames,
HasPrimaryOutput,
Locked,
NodeTypeMetadata,
// Node transient metadata
ClickTargets,
NodeTypeClickTargets,
// Derived metadata
Position,
IsLayer,
}
impl Clone for NodeNetworkInterface { impl Clone for NodeNetworkInterface {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
@@ -453,6 +484,24 @@ impl NodeNetworkInterface {
} }
} }
fn metadata_node_id(path: &[NodeId], metadata: Metadata) -> NodeId {
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
metadata.hash(&mut hasher);
NodeId(hasher.finish())
}
fn metadata_value(&self, path: &[NodeId], metadata: Metadata) -> Option<&TaggedValue> {
let node_id = Self::metadata_node_id(path, metadata);
let network = self.network(&[]).unwrap();
let Some(node) = network.nodes.get(&node_id) else {
log::error!("Could not get node {node_id} in value_from_node_id");
return None;
};
node.inputs.first().expect("Metadata node should always have primary input to store data").as_value()
}
/// Get the [`Type`] for any InputConnector /// Get the [`Type`] for any InputConnector
pub fn input_type(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Type { pub fn input_type(&self, input_connector: &InputConnector, network_path: &[NodeId]) -> Type {
// TODO: If the input_connector is a NodeInput::Value, return the type of the tagged value // TODO: If the input_connector is a NodeInput::Value, return the type of the tagged value
@@ -988,11 +1037,17 @@ impl NodeNetworkInterface {
} }
pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { pub fn display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
let Some(node_metadata) = self.node_metadata(node_id, network_path) else { let mut node_id_path = network_path.to_vec();
log::error!("Could not get node_metadata in display_name"); node_id_path.push(*node_id);
let Some(tagged_value, ..) = self.metadata_value(&node_id_path, Metadata::DisplayName) else {
log::error!("Could not get tagged value in display_name");
return "".to_string(); return "".to_string();
}; };
node_metadata.persistent_metadata.display_name.clone() let TaggedValue::String(display_name) = tagged_value else {
log::error!("Tagged value should be String in display_name");
return "".to_string();
};
display_name.clone()
} }
pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String { pub fn frontend_display_name(&self, node_id: &NodeId, network_path: &[NodeId]) -> String {
@@ -2212,7 +2267,7 @@ impl NodeNetworkInterface {
DocumentNodeClickTargets { DocumentNodeClickTargets {
node_click_target, node_click_target,
port_click_targets, port_click_targets,
node_type_metadata: NodeTypeClickTargets::Node, node_type_click_targets: NodeTypeClickTargets::Node,
} }
} else { } else {
// Layer inputs // Layer inputs
@@ -2251,7 +2306,7 @@ impl NodeNetworkInterface {
DocumentNodeClickTargets { DocumentNodeClickTargets {
node_click_target, node_click_target,
port_click_targets, port_click_targets,
node_type_metadata: NodeTypeClickTargets::Layer(LayerClickTargets { node_type_click_targets: NodeTypeClickTargets::Layer(LayerClickTargets {
visibility_click_target, visibility_click_target,
grip_click_target, grip_click_target,
}), }),
@@ -2450,7 +2505,7 @@ impl NodeNetworkInterface {
let _ = port.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY); let _ = port.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY);
port_click_targets.push(port_path); port_click_targets.push(port_path);
} }
if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_metadata { if let NodeTypeClickTargets::Layer(layer_metadata) = &node_click_targets.node_type_click_targets {
let mut port_path = String::new(); let mut port_path = String::new();
let _ = layer_metadata.visibility_click_target.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY); let _ = layer_metadata.visibility_click_target.subpath().subpath_to_svg(&mut port_path, DAffine2::IDENTITY);
icon_click_targets.push(port_path); icon_click_targets.push(port_path);
@@ -2601,7 +2656,7 @@ impl NodeNetworkInterface {
.iter() .iter()
.filter_map(|node_id| { .filter_map(|node_id| {
self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| { self.node_click_targets(node_id, network_path).and_then(|transient_node_metadata| {
if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_metadata { if let NodeTypeClickTargets::Layer(layer) = &transient_node_metadata.node_type_click_targets {
match click_target_type { match click_target_type {
LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id), LayerClickTargetTypes::Visibility => layer.visibility_click_target.intersect_point_no_stroke(point).then_some(*node_id),
LayerClickTargetTypes::Grip => layer.grip_click_target.intersect_point_no_stroke(point).then_some(*node_id), LayerClickTargetTypes::Grip => layer.grip_click_target.intersect_point_no_stroke(point).then_some(*node_id),
@@ -3390,6 +3445,8 @@ impl NodeNetworkInterface {
}; };
network.nodes.insert(node_id, node_template.document_node); network.nodes.insert(node_id, node_template.document_node);
// TODO: Remove this clone once the later usage is removed
self.insert_all_node_metadata(node_id, network_path, node_template.persistent_node_metadata.clone());
self.transaction_modified(); self.transaction_modified();
let Some(network_metadata) = self.network_metadata_mut(network_path) else { let Some(network_metadata) = self.network_metadata_mut(network_path) else {
@@ -3406,6 +3463,26 @@ impl NodeNetworkInterface {
self.unload_node_click_targets(&node_id, network_path) self.unload_node_click_targets(&node_id, network_path)
} }
fn insert_all_node_metadata(&mut self, node_id: NodeId, network_path: &[NodeId], persistent_metadata: DocumentNodePersistentMetadata) {
let mut node_path = network_path.to_vec();
node_path.push(node_id);
let display_name_node_id = Self::metadata_node_id(&node_path, Metadata::DisplayName);
self.insert_node_metadata(display_name_node_id, TaggedValue::String(persistent_metadata.display_name));
}
fn insert_node_metadata(&mut self, metadata_node_id: NodeId, tagged_value: TaggedValue) {
let network = self.network_mut(&[]).unwrap();
log::debug!("Inserting metadata node with id {metadata_node_id}");
network.nodes.insert(
metadata_node_id,
DocumentNode {
implementation: DocumentNodeImplementation::ProtoNode("graphene_core::value::ClonedNode".into()),
inputs: vec![NodeInput::value(tagged_value, true)],
..Default::default()
},
);
}
/// Deletes all nodes in `node_ids` and any sole dependents in the horizontal chain if the node to delete is a layer node. /// Deletes all nodes in `node_ids` and any sole dependents in the horizontal chain if the node to delete is a layer node.
pub fn delete_nodes(&mut self, nodes_to_delete: Vec<NodeId>, delete_children: bool, network_path: &[NodeId]) { pub fn delete_nodes(&mut self, nodes_to_delete: Vec<NodeId>, delete_children: bool, network_path: &[NodeId]) {
let Some(outward_wires) = self.outward_wires(network_path).cloned() else { let Some(outward_wires) = self.outward_wires(network_path).cloned() else {
@@ -3632,38 +3709,55 @@ impl NodeNetworkInterface {
// } // }
pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) { pub fn set_display_name(&mut self, node_id: &NodeId, display_name: String, network_path: &[NodeId]) {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else { let network = self.network_mut(network_path).unwrap();
log::error!("Could not get node {node_id} in set_visibility");
let mut node_path = network_path.to_vec();
node_path.push(*node_id);
let display_name_node_id = Self::metadata_node_id(&node_path, Metadata::DisplayName);
let Some(display_name_node) = network.nodes.get_mut(&display_name_node_id) else {
log::error!("Could not get display name node with id {display_name_node_id} in set_display_name");
return; return;
}; };
if node_metadata.persistent_metadata.display_name == display_name { let Some(display_name_input) = display_name_node.inputs.get_mut(0) else {
log::error!("Could not get display name input in set_display_name");
return;
};
let Some(TaggedValue::String(current_display_name)) = display_name_input.as_value() else {
log::error!("Could not get current display name in set_display_name");
return;
};
if *current_display_name == display_name {
return; return;
} }
node_metadata.persistent_metadata.display_name.clone_from(&display_name); *display_name_input = NodeInput::value(TaggedValue::String(display_name), false);
// Keep the alias in sync with the `ToArtboard` name input // TODO: Connect to the display name node. Commenting this out breaks https://github.com/GraphiteEditor/Graphite/issues/1706
if node_metadata.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard") { // Keep the display in sync with the `ToArtboard` name input
let Some(nested_network) = self.network_mut(network_path) else { // if node_metadata.persistent_metadata.reference.as_ref().is_some_and(|reference| reference == "Artboard") {
return; // let Some(nested_network) = self.network_mut(network_path) else {
}; // return;
let Some(artboard_node) = nested_network.nodes.get_mut(node_id) else { // };
return; // let Some(artboard_node) = nested_network.nodes.get_mut(node_id) else {
}; // return;
let DocumentNodeImplementation::Network(network) = &mut artboard_node.implementation else { // };
return; // let DocumentNodeImplementation::Network(network) = &mut artboard_node.implementation else {
}; // return;
// Keep this in sync with the definition // };
let Some(to_artboard) = network.nodes.get_mut(&NodeId(0)) else { // // Keep this in sync with the definition
return; // let Some(to_artboard) = network.nodes.get_mut(&NodeId(0)) else {
}; // return;
// };
let label_index = 1; // let label_index = 1;
let label = if !display_name.is_empty() { display_name } else { "Artboard".to_string() }; // let label = if !display_name.is_empty() { display_name } else { "Artboard".to_string() };
let label_input = NodeInput::value(TaggedValue::String(label), false); // let label_input = NodeInput::value(TaggedValue::String(label), false);
to_artboard.inputs[label_index] = label_input; // to_artboard.inputs[label_index] = label_input;
} // }
self.transaction_modified(); self.transaction_modified();
self.try_unload_layer_width(node_id, network_path); self.try_unload_layer_width(node_id, network_path);
@@ -5309,7 +5403,7 @@ pub struct DocumentNodeClickTargets {
/// Stores all port click targets in node graph space. /// Stores all port click targets in node graph space.
pub port_click_targets: Ports, pub port_click_targets: Ports,
// Click targets that are specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer. // Click targets that are specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer.
pub node_type_metadata: NodeTypeClickTargets, pub node_type_click_targets: NodeTypeClickTargets,
} }
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]