Fix using Esc to abort box selection and wire dragging causing the graph to close (#3409)

* Implement proper node graph interaction aborting when pressing Escape

* Fixes

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Yuxiang Huang
2025-12-05 01:42:00 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 2ee8e56cef
commit 8ca546c164
3 changed files with 45 additions and 18 deletions
@@ -325,7 +325,7 @@ pub fn input_mappings() -> Mapping {
// //
// DocumentMessage // DocumentMessage
entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle), entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle),
entry!(KeyUp(Escape); action_dispatch=DocumentMessage::Escape), entry!(KeyDownNoRepeat(Escape); action_dispatch=DocumentMessage::Escape),
entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers),
entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers),
entry!(KeyDown(KeyO); modifiers=[Alt], action_dispatch=DocumentMessage::ToggleOverlaysVisibility), entry!(KeyDown(KeyO); modifiers=[Alt], action_dispatch=DocumentMessage::ToggleOverlaysVisibility),
@@ -12,6 +12,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler}; use crate::messages::portfolio::document::data_panel::{DataPanelMessageContext, DataPanelMessageHandler};
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext; use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext;
use crate::messages::portfolio::document::node_graph::utility_types::FrontendGraphDataType;
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options}; use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings}; use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings};
use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext; use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext;
@@ -476,25 +477,44 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(NodeGraphMessage::UpdateNodeGraphWidth); responses.add(NodeGraphMessage::UpdateNodeGraphWidth);
} }
DocumentMessage::Escape => { DocumentMessage::Escape => {
// Abort dragging nodes
if self.node_graph_handler.drag_start.is_some() { if self.node_graph_handler.drag_start.is_some() {
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
self.node_graph_handler.drag_start = None; self.node_graph_handler.drag_start = None;
} else if self }
// Abort box selection
else if self.node_graph_handler.box_selection_start.is_some() {
self.node_graph_handler.box_selection_start = None;
responses.add(NodeGraphMessage::SelectedNodesSet {
nodes: self.node_graph_handler.selection_before_pointer_down.clone(),
});
responses.add(FrontendMessage::UpdateBox { box_selection: None });
}
// Abort wire in progress of being connected
else if self.node_graph_handler.wire_in_progress_from_connector.is_some() {
self.node_graph_handler.wire_in_progress_from_connector = None;
self.node_graph_handler.wire_in_progress_to_connector = None;
self.node_graph_handler.wire_in_progress_type = FrontendGraphDataType::General;
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(DocumentMessage::AbortTransaction);
}
// Close the context menu if it's open
else if self
.node_graph_handler .node_graph_handler
.context_menu .context_menu
.as_ref() .as_ref()
.is_some_and(|context_menu| matches!(context_menu.context_menu_data, super::node_graph::utility_types::ContextMenuData::CreateNode { compatible_type: None })) .is_some_and(|context_menu| matches!(context_menu.context_menu_data, super::node_graph::utility_types::ContextMenuData::CreateNode { compatible_type: None }))
{ {
// Close the context menu
self.node_graph_handler.context_menu = None; self.node_graph_handler.context_menu = None;
responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None }); responses.add(FrontendMessage::UpdateContextMenuInformation { context_menu_information: None });
self.node_graph_handler.wire_in_progress_from_connector = None; }
self.node_graph_handler.wire_in_progress_to_connector = None; // Exit one level up if inside a nested network
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); else if !self.breadcrumb_network_path.is_empty() {
} else if !self.breadcrumb_network_path.is_empty() {
// Exit one level up if inside a nested network
responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 }); responses.add(DocumentMessage::ExitNestedNetwork { steps_back: 1 });
} else { }
// Close the graph view overlay if it's open
else {
responses.add(DocumentMessage::GraphViewOverlay { open: false }); responses.add(DocumentMessage::GraphViewOverlay { open: false });
} }
} }
@@ -63,20 +63,21 @@ pub struct NodeGraphMessageHandler {
pub drag_start_chain_nodes: Vec<NodeId>, pub drag_start_chain_nodes: Vec<NodeId>,
/// If dragging the background to create a box selection, this stores its starting point in node graph coordinates, /// If dragging the background to create a box selection, this stores its starting point in node graph coordinates,
/// plus a flag indicating if it has been dragged since the mousedown began. /// plus a flag indicating if it has been dragged since the mousedown began.
box_selection_start: Option<(DVec2, bool)>, pub box_selection_start: Option<(DVec2, bool)>,
/// Restore the selection before box selection if it is aborted /// Restore the selection before box selection if it is aborted
selection_before_pointer_down: Vec<NodeId>, pub selection_before_pointer_down: Vec<NodeId>,
/// If the grip icon is held during a drag, then shift without pushing other nodes /// If the grip icon is held during a drag, then shift without pushing other nodes
shift_without_push: bool, shift_without_push: bool,
disconnecting: Option<InputConnector>, disconnecting: Option<InputConnector>,
initial_disconnecting: bool, initial_disconnecting: bool,
/// Node to select on pointer up if multiple nodes are selected and they were not dragged. /// Node to select on pointer up if multiple nodes are selected and they were not dragged.
select_if_not_dragged: Option<NodeId>, select_if_not_dragged: Option<NodeId>,
/// The start of the dragged line (cannot be moved), stored in node graph coordinates /// The start of the dragged line (cannot be moved), stored in node graph coordinates.
pub wire_in_progress_from_connector: Option<DVec2>, pub wire_in_progress_from_connector: Option<DVec2>,
wire_in_progress_type: FrontendGraphDataType, /// The end point of the dragged line (cannot be moved), stored in node graph coordinates.
/// The end point of the dragged line (cannot be moved), stored in node graph coordinates
pub wire_in_progress_to_connector: Option<DVec2>, pub wire_in_progress_to_connector: Option<DVec2>,
/// The data type determining the color of the wire being dragged.
pub wire_in_progress_type: FrontendGraphDataType,
/// State for the context menu popups. /// State for the context menu popups.
pub context_menu: Option<ContextMenuInformation>, pub context_menu: Option<ContextMenuInformation>,
/// Index of selected node to be deselected on pointer up when shift clicking an already selected node /// Index of selected node to be deselected on pointer up when shift clicking an already selected node
@@ -295,8 +296,8 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
} }
self.wire_in_progress_from_connector = None; self.wire_in_progress_from_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
} }
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
@@ -768,8 +769,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
// Abort dragging a wire // Abort dragging a wire
if self.wire_in_progress_from_connector.is_some() { if self.wire_in_progress_from_connector.is_some() {
self.wire_in_progress_from_connector = None; self.wire_in_progress_from_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
return; return;
@@ -850,8 +852,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
if self.context_menu.is_some() { if self.context_menu.is_some() {
self.context_menu = None; self.context_menu = None;
self.wire_in_progress_from_connector = None; self.wire_in_progress_from_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
responses.add(FrontendMessage::UpdateContextMenuInformation { responses.add(FrontendMessage::UpdateContextMenuInformation {
context_menu_information: self.context_menu.clone(), context_menu_information: self.context_menu.clone(),
}); });
@@ -1388,14 +1391,18 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeG
}); });
responses.add(DocumentMessage::EndTransaction); responses.add(DocumentMessage::EndTransaction);
} }
self.drag_start = None; self.drag_start = None;
self.begin_dragging = false; self.begin_dragging = false;
self.box_selection_start = None; self.box_selection_start = None;
self.wire_in_progress_from_connector = None; self.wire_in_progress_from_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
self.wire_in_progress_to_connector = None; self.wire_in_progress_to_connector = None;
self.wire_in_progress_type = FrontendGraphDataType::General;
self.reordering_export = None; self.reordering_export = None;
self.reordering_import = None; self.reordering_import = None;
responses.add(DocumentMessage::EndTransaction); responses.add(DocumentMessage::EndTransaction);
responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None }); responses.add(FrontendMessage::UpdateWirePathInProgress { wire_path: None });
responses.add(FrontendMessage::UpdateBox { box_selection: None }); responses.add(FrontendMessage::UpdateBox { box_selection: None });