-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Problem Description
When multiple nodes are selected and a user clicks on a new node (without Ctrl), the previously selected nodes are deselected but never report this change via NodeResponse::changed(). This causes downstream systems that rely on changed() to track selection state to become out of sync with egui_graph's internal selection.
Root Cause
The issue occurs in Node::show_impl() (src/node.rs:377-380) when handling node selection:
// Clear other selections if ctrl is not pressed and this is newly pressed.
if !ctrl_down && !was_selected {
gmem.selection.nodes.clear(); // Clears ALL other selected nodes
}
selection_changed = gmem.selection.nodes.insert(self.id); // Only tracks THIS node's changeWhen gmem.selection.nodes.clear() is called:
- All other nodes are immediately deselected in the shared
GraphTempMemory - Only the clicked node knows it was selected and sets
selection_changed = true - Other deselected nodes never report
changed()because:- Nodes processed before the clicked node already ran and saw themselves as selected
- Nodes processed after the clicked node see themselves as deselected but have no way to know they were selected at the start of the frame
Proposed Solution
The fundamental issue is that selection is a graph-level concern, but changes are reported at the node level. Since nodes are processed sequentially, they cannot reliably report selection changes that affect other nodes.
Option 1: Introduce GraphResponse (Recommended)
Add a new GraphResponse type returned by Graph::show() that reports graph-level changes:
pub struct GraphResponse {
/// Nodes that had their selection state change this frame
pub selection_changed: HashSet<NodeId>,
/// The current selection (for convenience)
pub selected_nodes: HashSet<NodeId>,
/// Nodes that were removed this frame
pub removed_nodes: HashSet<NodeId>,
// ... other graph-level events
}
impl Graph {
pub fn show(self, ui: &mut egui::Ui, view: &mut View) -> GraphResponse {
// Track selection changes at the graph level
let previous_selection = /* snapshot at frame start */;
// ... existing show logic ...
let current_selection = /* current selection state */;
let selection_changed = previous_selection.symmetric_difference(¤t_selection);
GraphResponse {
selection_changed: selection_changed.cloned().collect(),
selected_nodes: current_selection,
removed_nodes,
// ...
}
}
}This would allow applications to handle selection changes reliably:
let graph_response = graph.show(ui, &mut view);
// Update application's selection state
for node_id in &graph_response.selection_changed {
if graph_response.selected_nodes.contains(node_id) {
app_state.selection.insert(*node_id);
} else {
app_state.selection.remove(node_id);
}
}