mirror of
https://github.com/eliasstepanik/egui_node_graph.git
synced 2026-01-20 02:08:29 +00:00
Improve the deleted node events with extra information
This commit is contained in:
parent
b9ed8bd5ab
commit
16ea6e1265
@ -12,8 +12,8 @@ pub type PortLocations = std::collections::HashMap<AnyParameterId, Pos2>;
|
|||||||
/// Nodes communicate certain events to the parent graph when drawn. There is
|
/// Nodes communicate certain events to the parent graph when drawn. There is
|
||||||
/// one special `User` variant which can be used by users as the return value
|
/// one special `User` variant which can be used by users as the return value
|
||||||
/// when executing some custom actions in the UI of the node.
|
/// when executing some custom actions in the UI of the node.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum NodeResponse<UserResponse: UserResponseTrait> {
|
pub enum NodeResponse<UserResponse: UserResponseTrait, NodeData: NodeDataTrait> {
|
||||||
ConnectEventStarted(NodeId, AnyParameterId),
|
ConnectEventStarted(NodeId, AnyParameterId),
|
||||||
ConnectEventEnded {
|
ConnectEventEnded {
|
||||||
output: OutputId,
|
output: OutputId,
|
||||||
@ -21,7 +21,16 @@ pub enum NodeResponse<UserResponse: UserResponseTrait> {
|
|||||||
},
|
},
|
||||||
CreatedNode(NodeId),
|
CreatedNode(NodeId),
|
||||||
SelectNode(NodeId),
|
SelectNode(NodeId),
|
||||||
DeleteNode(NodeId),
|
/// As a user of this library, prefer listening for `DeleteNodeFull` which
|
||||||
|
/// will also contain the user data for the deleted node.
|
||||||
|
DeleteNodeUi(NodeId),
|
||||||
|
/// Emitted when a node is deleted. The node will no longer exist in the
|
||||||
|
/// graph after this response is returned from the draw function, but its
|
||||||
|
/// contents are passed along with the event.
|
||||||
|
DeleteNodeFull {
|
||||||
|
node_id: NodeId,
|
||||||
|
node: Node<NodeData>,
|
||||||
|
},
|
||||||
DisconnectEvent {
|
DisconnectEvent {
|
||||||
output: OutputId,
|
output: OutputId,
|
||||||
input: InputId,
|
input: InputId,
|
||||||
@ -34,8 +43,8 @@ pub enum NodeResponse<UserResponse: UserResponseTrait> {
|
|||||||
/// The return value of [`draw_graph_editor`]. This value can be used to make
|
/// The return value of [`draw_graph_editor`]. This value can be used to make
|
||||||
/// user code react to specific events that happened when drawing the graph.
|
/// user code react to specific events that happened when drawing the graph.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GraphResponse<UserResponse: UserResponseTrait> {
|
pub struct GraphResponse<UserResponse: UserResponseTrait, NodeData: NodeDataTrait> {
|
||||||
pub node_responses: Vec<NodeResponse<UserResponse>>,
|
pub node_responses: Vec<NodeResponse<UserResponse, NodeData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GraphNodeWidget<'a, NodeData, DataType, ValueType> {
|
pub struct GraphNodeWidget<'a, NodeData, DataType, ValueType> {
|
||||||
@ -68,7 +77,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
all_kinds: impl NodeTemplateIter<Item = NodeTemplate>,
|
all_kinds: impl NodeTemplateIter<Item = NodeTemplate>,
|
||||||
) -> GraphResponse<UserResponse> {
|
) -> GraphResponse<UserResponse, NodeData> {
|
||||||
// This causes the graph editor to use as much free space as it can.
|
// This causes the graph editor to use as much free space as it can.
|
||||||
// (so for windows it will use up to the resizeably set limit
|
// (so for windows it will use up to the resizeably set limit
|
||||||
// and for a Panel it will fill it completely)
|
// and for a Panel it will fill it completely)
|
||||||
@ -83,7 +92,7 @@ where
|
|||||||
|
|
||||||
// The responses returned from node drawing have side effects that are best
|
// The responses returned from node drawing have side effects that are best
|
||||||
// executed at the end of this function.
|
// executed at the end of this function.
|
||||||
let mut delayed_responses: Vec<NodeResponse<UserResponse>> = vec![];
|
let mut delayed_responses: Vec<NodeResponse<UserResponse, NodeData>> = vec![];
|
||||||
|
|
||||||
// Used to detect when the background was clicked, to dismiss certain selfs
|
// Used to detect when the background was clicked, to dismiss certain selfs
|
||||||
let mut click_on_background = false;
|
let mut click_on_background = false;
|
||||||
@ -182,54 +191,63 @@ where
|
|||||||
|
|
||||||
// Some responses generate additional responses when processed. These
|
// Some responses generate additional responses when processed. These
|
||||||
// are stored here to report them back to the user.
|
// are stored here to report them back to the user.
|
||||||
let mut extra_responses: Vec<NodeResponse<UserResponse>> = Vec::new();
|
let mut extra_responses: Vec<NodeResponse<UserResponse, NodeData>> = Vec::new();
|
||||||
|
|
||||||
for response in delayed_responses.iter().copied() {
|
for response in delayed_responses.iter() {
|
||||||
match response {
|
match response {
|
||||||
NodeResponse::ConnectEventStarted(node_id, port) => {
|
NodeResponse::ConnectEventStarted(node_id, port) => {
|
||||||
self.connection_in_progress = Some((node_id, port));
|
self.connection_in_progress = Some((*node_id, *port));
|
||||||
}
|
}
|
||||||
NodeResponse::ConnectEventEnded { input, output } => {
|
NodeResponse::ConnectEventEnded { input, output } => {
|
||||||
self.graph.add_connection(output, input)
|
self.graph.add_connection(*output, *input)
|
||||||
}
|
}
|
||||||
NodeResponse::CreatedNode(_) => {
|
NodeResponse::CreatedNode(_) => {
|
||||||
//Convenience NodeResponse for users
|
//Convenience NodeResponse for users
|
||||||
}
|
}
|
||||||
NodeResponse::SelectNode(node_id) => {
|
NodeResponse::SelectNode(node_id) => {
|
||||||
self.selected_node = Some(node_id);
|
self.selected_node = Some(*node_id);
|
||||||
}
|
}
|
||||||
NodeResponse::DeleteNode(node_id) => {
|
NodeResponse::DeleteNodeUi(node_id) => {
|
||||||
let removed = self.graph.remove_node(node_id);
|
let (node, disc_events) = self.graph.remove_node(*node_id);
|
||||||
|
// Pass the full node as a response so library users can
|
||||||
|
// listen for it and get their user data.
|
||||||
|
extra_responses.push(NodeResponse::DeleteNodeFull {
|
||||||
|
node_id: *node_id,
|
||||||
|
node,
|
||||||
|
});
|
||||||
extra_responses.extend(
|
extra_responses.extend(
|
||||||
removed
|
disc_events
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(input, output)| NodeResponse::DisconnectEvent { input, output }),
|
.map(|(input, output)| NodeResponse::DisconnectEvent { input, output }),
|
||||||
);
|
);
|
||||||
self.node_positions.remove(node_id);
|
self.node_positions.remove(*node_id);
|
||||||
// Make sure to not leave references to old nodes hanging
|
// Make sure to not leave references to old nodes hanging
|
||||||
if self.selected_node.map(|x| x == node_id).unwrap_or(false) {
|
if self.selected_node.map(|x| x == *node_id).unwrap_or(false) {
|
||||||
self.selected_node = None;
|
self.selected_node = None;
|
||||||
}
|
}
|
||||||
self.node_order.retain(|id| *id != node_id);
|
self.node_order.retain(|id| *id != *node_id);
|
||||||
}
|
}
|
||||||
NodeResponse::DisconnectEvent { input, output } => {
|
NodeResponse::DisconnectEvent { input, output } => {
|
||||||
let other_node = self.graph.get_input(input).node();
|
let other_node = self.graph.get_input(*input).node();
|
||||||
self.graph.remove_connection(input);
|
self.graph.remove_connection(*input);
|
||||||
self.connection_in_progress =
|
self.connection_in_progress =
|
||||||
Some((other_node, AnyParameterId::Output(output)));
|
Some((other_node, AnyParameterId::Output(*output)));
|
||||||
}
|
}
|
||||||
NodeResponse::RaiseNode(node_id) => {
|
NodeResponse::RaiseNode(node_id) => {
|
||||||
let old_pos = self
|
let old_pos = self
|
||||||
.node_order
|
.node_order
|
||||||
.iter()
|
.iter()
|
||||||
.position(|id| *id == node_id)
|
.position(|id| *id == *node_id)
|
||||||
.expect("Node to be raised should be in `node_order`");
|
.expect("Node to be raised should be in `node_order`");
|
||||||
self.node_order.remove(old_pos);
|
self.node_order.remove(old_pos);
|
||||||
self.node_order.push(node_id);
|
self.node_order.push(*node_id);
|
||||||
}
|
}
|
||||||
NodeResponse::User(_) => {
|
NodeResponse::User(_) => {
|
||||||
// These are handled by the user code.
|
// These are handled by the user code.
|
||||||
}
|
}
|
||||||
|
NodeResponse::DeleteNodeFull { .. } => {
|
||||||
|
unreachable!("The UI should never produce a DeleteNodeFull event.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +321,11 @@ where
|
|||||||
{
|
{
|
||||||
pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0];
|
pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0];
|
||||||
|
|
||||||
pub fn show(self, ui: &mut Ui, user_state: &UserState) -> Vec<NodeResponse<UserResponse>> {
|
pub fn show(
|
||||||
|
self,
|
||||||
|
ui: &mut Ui,
|
||||||
|
user_state: &UserState,
|
||||||
|
) -> Vec<NodeResponse<UserResponse, NodeData>> {
|
||||||
let mut child_ui = ui.child_ui_with_id_source(
|
let mut child_ui = ui.child_ui_with_id_source(
|
||||||
Rect::from_min_size(*self.position + self.pan, Self::MAX_NODE_SIZE.into()),
|
Rect::from_min_size(*self.position + self.pan, Self::MAX_NODE_SIZE.into()),
|
||||||
Layout::default(),
|
Layout::default(),
|
||||||
@ -319,9 +341,9 @@ where
|
|||||||
self,
|
self,
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
user_state: &UserState,
|
user_state: &UserState,
|
||||||
) -> Vec<NodeResponse<UserResponse>> {
|
) -> Vec<NodeResponse<UserResponse, NodeData>> {
|
||||||
let margin = egui::vec2(15.0, 5.0);
|
let margin = egui::vec2(15.0, 5.0);
|
||||||
let mut responses = Vec::new();
|
let mut responses = Vec::<NodeResponse<UserResponse, NodeData>>::new();
|
||||||
|
|
||||||
let background_color;
|
let background_color;
|
||||||
let text_color;
|
let text_color;
|
||||||
@ -415,7 +437,7 @@ where
|
|||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
user_state: &UserState,
|
user_state: &UserState,
|
||||||
port_pos: Pos2,
|
port_pos: Pos2,
|
||||||
responses: &mut Vec<NodeResponse<UserResponse>>,
|
responses: &mut Vec<NodeResponse<UserResponse, NodeData>>,
|
||||||
param_id: AnyParameterId,
|
param_id: AnyParameterId,
|
||||||
port_locations: &mut PortLocations,
|
port_locations: &mut PortLocations,
|
||||||
ongoing_drag: Option<(NodeId, AnyParameterId)>,
|
ongoing_drag: Option<(NodeId, AnyParameterId)>,
|
||||||
@ -423,6 +445,7 @@ where
|
|||||||
) where
|
) where
|
||||||
DataType: DataTypeTrait<UserState>,
|
DataType: DataTypeTrait<UserState>,
|
||||||
UserResponse: UserResponseTrait,
|
UserResponse: UserResponseTrait,
|
||||||
|
NodeData: NodeDataTrait,
|
||||||
{
|
{
|
||||||
let port_type = graph.any_param_type(param_id).unwrap();
|
let port_type = graph.any_param_type(param_id).unwrap();
|
||||||
|
|
||||||
@ -596,7 +619,7 @@ where
|
|||||||
|
|
||||||
// Titlebar buttons
|
// Titlebar buttons
|
||||||
if Self::close_button(ui, outer_rect).clicked() {
|
if Self::close_button(ui, outer_rect).clicked() {
|
||||||
responses.push(NodeResponse::DeleteNode(self.node_id));
|
responses.push(NodeResponse::DeleteNodeUi(self.node_id));
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_response = ui.interact(
|
let window_response = ui.interact(
|
||||||
|
|||||||
@ -53,6 +53,20 @@ impl<NodeData, DataType, ValueType> Graph<NodeData, DataType, ValueType> {
|
|||||||
input_id
|
input_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_input_param(&mut self, param: InputId) {
|
||||||
|
let node = self[param].node;
|
||||||
|
self[node].inputs.retain(|(_, id)| *id != param);
|
||||||
|
self.inputs.remove(param);
|
||||||
|
self.connections.retain(|i, _| i != param);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_output_param(&mut self, param: OutputId) {
|
||||||
|
let node = self[param].node;
|
||||||
|
self[node].outputs.retain(|(_, id)| *id != param);
|
||||||
|
self.outputs.remove(param);
|
||||||
|
self.connections.retain(|_, o| *o != param);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_output_param(&mut self, node_id: NodeId, name: String, typ: DataType) -> OutputId {
|
pub fn add_output_param(&mut self, node_id: NodeId, name: String, typ: DataType) -> OutputId {
|
||||||
let output_id = self.outputs.insert_with_key(|output_id| OutputParam {
|
let output_id = self.outputs.insert_with_key(|output_id| OutputParam {
|
||||||
id: output_id,
|
id: output_id,
|
||||||
@ -70,7 +84,7 @@ impl<NodeData, DataType, ValueType> Graph<NodeData, DataType, ValueType> {
|
|||||||
/// after deleting this node as input-output pairs. Note that one of the two
|
/// after deleting this node as input-output pairs. Note that one of the two
|
||||||
/// ids in the pair (the one on `node_id`'s end) will be invalid after
|
/// ids in the pair (the one on `node_id`'s end) will be invalid after
|
||||||
/// calling this function.
|
/// calling this function.
|
||||||
pub fn remove_node(&mut self, node_id: NodeId) -> Vec<(InputId, OutputId)> {
|
pub fn remove_node(&mut self, node_id: NodeId) -> (Node<NodeData>, Vec<(InputId, OutputId)>) {
|
||||||
let mut disconnect_events = vec![];
|
let mut disconnect_events = vec![];
|
||||||
|
|
||||||
self.connections.retain(|i, o| {
|
self.connections.retain(|i, o| {
|
||||||
@ -90,9 +104,9 @@ impl<NodeData, DataType, ValueType> Graph<NodeData, DataType, ValueType> {
|
|||||||
for output in self[node_id].output_ids().collect::<SVec<_>>() {
|
for output in self[node_id].output_ids().collect::<SVec<_>>() {
|
||||||
self.outputs.remove(output);
|
self.outputs.remove(output);
|
||||||
}
|
}
|
||||||
self.nodes.remove(node_id);
|
let removed_node = self.nodes.remove(node_id).expect("Node should exist");
|
||||||
|
|
||||||
disconnect_events
|
(removed_node, disconnect_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_connection(&mut self, input_id: InputId) -> Option<OutputId> {
|
pub fn remove_connection(&mut self, input_id: InputId) -> Option<OutputId> {
|
||||||
@ -123,10 +137,18 @@ impl<NodeData, DataType, ValueType> Graph<NodeData, DataType, ValueType> {
|
|||||||
.ok_or(EguiGraphError::InvalidParameterId(param))
|
.ok_or(EguiGraphError::InvalidParameterId(param))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_get_input(&self, input: InputId) -> Option<&InputParam<DataType, ValueType>> {
|
||||||
|
self.inputs.get(input)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_input(&self, input: InputId) -> &InputParam<DataType, ValueType> {
|
pub fn get_input(&self, input: InputId) -> &InputParam<DataType, ValueType> {
|
||||||
&self.inputs[input]
|
&self.inputs[input]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_get_output(&self, output: OutputId) -> Option<&OutputParam<DataType>> {
|
||||||
|
self.outputs.get(output)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_output(&self, output: OutputId) -> &OutputParam<DataType> {
|
pub fn get_output(&self, output: OutputId) -> &OutputParam<DataType> {
|
||||||
&self.outputs[output]
|
&self.outputs[output]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,7 +72,7 @@ where
|
|||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
graph: &Graph<Self, Self::DataType, Self::ValueType>,
|
graph: &Graph<Self, Self::DataType, Self::ValueType>,
|
||||||
user_state: &Self::UserState,
|
user_state: &Self::UserState,
|
||||||
) -> Vec<NodeResponse<Self::Response>>
|
) -> Vec<NodeResponse<Self::Response, Self>>
|
||||||
where
|
where
|
||||||
Self::Response: UserResponseTrait;
|
Self::Response: UserResponseTrait;
|
||||||
|
|
||||||
@ -130,4 +130,4 @@ pub trait NodeTemplateTrait: Clone {
|
|||||||
|
|
||||||
/// The custom user response types when drawing nodes in the graph must
|
/// The custom user response types when drawing nodes in the graph must
|
||||||
/// implement this trait.
|
/// implement this trait.
|
||||||
pub trait UserResponseTrait: Clone + Copy + std::fmt::Debug + PartialEq + Eq {}
|
pub trait UserResponseTrait: Clone + std::fmt::Debug {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user