Add support for pinning nodes in the Properties panel

This commit is contained in:
Keavon Chambers 2024-10-03 16:26:40 -07:00
parent b26dfbcd7c
commit e6d8c4743d
4 changed files with 113 additions and 12 deletions

View File

@ -382,8 +382,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
let name = self.network_interface.frontend_display_name(&layer.to_node(), &[]);
let (_, angle, translation) = self.metadata().document_to_viewport.to_scale_angle_translation();
let translation = translation + bounds[0].min(bounds[1]) - DVec2::Y * 4.;
let (scale, angle, translation) = self.metadata().document_to_viewport.to_scale_angle_translation();
let translation = translation + scale * bounds[0].min(bounds[1]) - DVec2::Y * 4.;
let transform = DAffine2::from_angle_translation(angle, translation);
overlay_context.text_with_transform(&name, COLOR_OVERLAY_GRAY, None, transform, Pivot::BottomLeft);

View File

@ -139,10 +139,15 @@ pub enum NodeGraphMessage {
node_id: NodeId,
locked: bool,
},
ToggleSelectedIsPinned,
ToggleSelectedVisibility,
ToggleVisibility {
node_id: NodeId,
},
SetPinned {
node_id: NodeId,
pinned: bool,
},
SetVisibility {
node_id: NodeId,
visible: bool,

View File

@ -1219,6 +1219,28 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
NodeGraphMessage::SetLocked { node_id, locked } => {
network_interface.set_locked(&node_id, selection_network_path, locked);
}
NodeGraphMessage::ToggleSelectedIsPinned => {
let Some(selected_nodes) = network_interface.selected_nodes(selection_network_path) else {
log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedIsPinned");
return;
};
let node_ids = selected_nodes.selected_nodes().cloned().collect::<Vec<_>>();
// If any of the selected nodes are pinned, unpin them all. Otherwise, pin them all.
let pinned = !node_ids.iter().all(|node_id| {
if let Some(node) = network_interface.node_metadata(node_id, breadcrumb_network_path) {
node.persistent_metadata.pinned
} else {
false
}
});
responses.add(DocumentMessage::AddTransaction);
for node_id in &node_ids {
responses.add(NodeGraphMessage::SetPinned { node_id: *node_id, pinned });
}
responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids });
}
NodeGraphMessage::ToggleSelectedVisibility => {
let Some(network) = network_interface.network(selection_network_path) else {
return;
@ -1227,7 +1249,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
log::error!("Could not get selected nodes in NodeGraphMessage::ToggleSelectedLocked");
return;
};
let node_ids = selected_nodes.selected_nodes().cloned().collect::<Vec<_>>();
// If any of the selected nodes are hidden, show them all. Otherwise, hide them all.
@ -1255,6 +1276,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SetVisibility { node_id, visible });
responses.add(NodeGraphMessage::SetLockedOrVisibilitySideEffects { node_ids: vec![node_id] });
}
NodeGraphMessage::SetPinned { node_id, pinned } => {
network_interface.set_pinned(&node_id, selection_network_path, pinned);
}
NodeGraphMessage::SetVisibility { node_id, visible } => {
network_interface.set_visibility(&node_id, selection_network_path, visible);
}
@ -1459,8 +1483,7 @@ impl NodeGraphMessageHandler {
};
let mut selection = selected_nodes.selected_nodes();
// If there is at least one other selected node then show the hide or show button
// If there is at least one selected node then show the hide or show button
if selection.next().is_some() {
// Check if any of the selected nodes are disabled
let all_visible = selected_nodes.selected_nodes().all(|id| {
@ -1479,7 +1502,7 @@ impl NodeGraphMessageHandler {
let (hide_show_label, hide_show_icon) = if all_visible { ("Make Hidden", "EyeVisible") } else { ("Make Visible", "EyeHidden") };
let hide_button = TextButton::new(hide_show_label)
.icon(Some(hide_show_icon.to_string()))
.tooltip(if all_visible { "Hide selected nodes/layers" } else { "Show selected nodes/layers" }.to_string() + if multiple_nodes { "s" } else { "" })
.tooltip(if all_visible { "Hide" } else { "Show" }.to_string() + " selected " + if multiple_nodes { "nodes/layers" } else { "node/layer" })
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
.on_update(move |_| NodeGraphMessage::ToggleSelectedVisibility.into())
.widget_holder();
@ -1488,6 +1511,39 @@ impl NodeGraphMessageHandler {
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
}
let mut selection = selected_nodes.selected_nodes();
// If there is at least one selected node then show the pin or unpin button
if selection.next().is_some() {
// Check if any of the selected nodes are pinned
let all_unpinned = !selected_nodes.selected_nodes().all(|id| {
if let Some(node) = network_interface.node_metadata(id, breadcrumb_network_path) {
node.persistent_metadata.pinned
} else {
error!("Could not get node {id} in update_selection_action_buttons");
false
}
});
// Check if multiple nodes are selected
let multiple_nodes = selection.next().is_some();
// Generate the visible/hidden button accordingly
let (pin_unpin_label, pin_unpin_icon) = if all_unpinned { ("Pin", "CheckboxUnchecked") } else { ("Unpin", "CheckboxChecked") };
let pin_button = TextButton::new(pin_unpin_label)
.icon(Some(pin_unpin_icon.to_string()))
.tooltip(
if all_unpinned { "Pin" } else { "Unpin" }.to_string()
+ " selected " + if multiple_nodes { "nodes/layers" } else { "node/layer" }
+ " in the Properties panel when nothing is selected",
)
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedIsPinned))
.on_update(move |_| NodeGraphMessage::ToggleSelectedIsPinned.into())
.widget_holder();
widgets.push(pin_button);
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
}
let mut selection = selected_nodes.selected_nodes();
// If only one node is selected then show the preview or stop previewing button
if let (Some(&node_id), None) = (selection.next(), selection.next()) {
@ -1518,10 +1574,12 @@ impl NodeGraphMessageHandler {
warn!("No selected nodes in collate_properties");
return Vec::new();
};
// We want:
// - If only nodes (no layers) are selected: display each node's properties
// - If one layer is selected, and zero or more of its upstream nodes: display the properties for the layer and its upstream nodes
// - If one layer is selected, and zero or more of its (primary flow) upstream nodes: display the properties for the layer and all its upstream nodes
// - If multiple layers are selected, or one node plus other non-upstream nodes: display nothing
// - If nothing is selected, display any pinned nodes/layers
// First, we filter all the selections into layers and nodes
let (mut layers, mut nodes) = (Vec::new(), Vec::new());
@ -1536,10 +1594,35 @@ impl NodeGraphMessageHandler {
// Next, we decide what to display based on the number of layers and nodes selected
match layers.len() {
// If no layers are selected, show properties for all selected nodes
0 => nodes
.iter()
.filter_map(|node_id| network.nodes.get(node_id).map(|node| node_properties::generate_node_properties(node, *node_id, context)))
.collect(),
0 => {
let selected_nodes = nodes
.iter()
.filter_map(|node_id| network.nodes.get(node_id).map(|node| node_properties::generate_node_properties(node, *node_id, context)))
.collect::<Vec<_>>();
if !selected_nodes.is_empty() {
return selected_nodes;
}
// And if no nodes are selected, show properties for all pinned nodes
network
.nodes
.iter()
.filter_map(|(node_id, node)| {
let pinned = if let Some(node) = context.network_interface.node_metadata(node_id, context.selection_network_path) {
node.persistent_metadata.pinned
} else {
error!("Could not get node {node_id} in collate_properties");
false
};
if pinned {
Some(node_properties::generate_node_properties(node, *node_id, context))
} else {
None
}
})
.collect::<Vec<_>>()
}
// If one layer is selected, filter out all selected nodes that are not upstream of it. If there are no nodes left, show properties for the layer. Otherwise, show nothing.
1 => {
let nodes_not_upstream_of_layer = nodes.into_iter().filter(|&selected_node_id| {

View File

@ -3749,11 +3749,20 @@ impl NodeNetworkInterface {
self.unload_node_click_targets(node_id, network_path);
}
pub fn set_pinned(&mut self, node_id: &NodeId, network_path: &[NodeId], pinned: bool) {
let Some(node_metadata) = self.node_metadata_mut(node_id, network_path) else {
log::error!("Could not get node {node_id} in set_pinned");
return;
};
node_metadata.persistent_metadata.pinned = pinned;
self.transaction_modified();
}
pub fn set_visibility(&mut self, node_id: &NodeId, network_path: &[NodeId], is_visible: bool) {
let Some(network) = self.network_mut(network_path) else {
return;
};
let Some(node) = network.nodes.get_mut(node_id) else {
log::error!("Could not get node {node_id} in set_visibility");
return;
@ -5301,6 +5310,9 @@ pub struct DocumentNodePersistentMetadata {
/// Represents the lock icon for locking/unlocking the node in the graph UI. When locked, a node cannot be moved in the graph UI.
#[serde(default)]
pub locked: bool,
/// Indicates that the node will be shown in the Properties panel when it would otherwise be empty, letting a user easily edit its properties by just deselecting everything.
#[serde(default)]
pub pinned: bool,
/// Metadata that is specific to either nodes or layers, which are chosen states for displaying as a left-to-right node or bottom-to-top layer.
/// All fields in NodeTypePersistentMetadata should automatically be updated by using the network interface API
pub node_type_metadata: NodeTypePersistentMetadata,
@ -5316,6 +5328,7 @@ impl Default for DocumentNodePersistentMetadata {
input_names: Vec::new(),
output_names: Vec::new(),
has_primary_output: true,
pinned: false,
locked: false,
node_type_metadata: NodeTypePersistentMetadata::default(),
network_metadata: None,