From aecb0f5031678c6b9ba73579802f3ca261fb3360 Mon Sep 17 00:00:00 2001 From: setzer22 Date: Sun, 29 May 2022 18:35:30 +0200 Subject: [PATCH 1/9] Send disconnect event on node delete --- egui_node_graph/src/editor_ui.rs | 12 +++++++++++- egui_node_graph/src/graph_impls.rs | 12 +++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index 5a725cb..e2b949c 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -168,6 +168,10 @@ where /* Handle responses from drawing nodes */ + // Some responses generate additional responses when processed. These + // are stored here to report them back to the user. + let mut extra_responses : Vec> = Vec::new(); + for response in delayed_responses.iter().copied() { match response { NodeResponse::ConnectEventStarted(node_id, port) => { @@ -199,7 +203,8 @@ where self.selected_node = Some(node_id); } NodeResponse::DeleteNode(node_id) => { - self.graph.remove_node(node_id); + let (ins, _) = self.graph.remove_node(node_id); + extra_responses.extend(ins.into_iter().map(NodeResponse::DisconnectEvent)); self.node_positions.remove(node_id); // Make sure to not leave references to old nodes hanging if self.selected_node.map(|x| x == node_id).unwrap_or(false) { @@ -232,6 +237,11 @@ where } } + // Push any responses that were generated during response handling. + // These are only informative for the end-user and need no special + // treatment here. + delayed_responses.extend(extra_responses); + /* Mouse input handling */ // This locks the context, so don't hold on to it for too long. diff --git a/egui_node_graph/src/graph_impls.rs b/egui_node_graph/src/graph_impls.rs index 7f898a6..42cd96e 100644 --- a/egui_node_graph/src/graph_impls.rs +++ b/egui_node_graph/src/graph_impls.rs @@ -63,18 +63,20 @@ impl Graph { output_id } - pub fn remove_node(&mut self, node_id: NodeId) { + /// Returns the list of input and output ids that were disconnected. + pub fn remove_node(&mut self, node_id: NodeId) -> (SVec, SVec) { self.connections .retain(|i, o| !(self.outputs[*o].node == node_id || self.inputs[i].node == node_id)); let inputs: SVec<_> = self[node_id].input_ids().collect(); - for input in inputs { - self.inputs.remove(input); + for input in &inputs { + self.inputs.remove(*input); } let outputs: SVec<_> = self[node_id].output_ids().collect(); - for output in outputs { - self.outputs.remove(output); + for output in &outputs { + self.outputs.remove(*output); } self.nodes.remove(node_id); + (inputs, outputs) } pub fn remove_connection(&mut self, input_id: InputId) -> Option { From 2ca80217877bc576f3bb67b44940bc6e732abc8c Mon Sep 17 00:00:00 2001 From: setzer22 Date: Sun, 29 May 2022 18:37:39 +0200 Subject: [PATCH 2/9] Cargo fmt --- egui_node_graph/src/editor_ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index e2b949c..4896b04 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -170,7 +170,7 @@ where // Some responses generate additional responses when processed. These // are stored here to report them back to the user. - let mut extra_responses : Vec> = Vec::new(); + let mut extra_responses: Vec> = Vec::new(); for response in delayed_responses.iter().copied() { match response { From 72d1001a3f5adfd305181b10b85a7a632b999a68 Mon Sep 17 00:00:00 2001 From: KOKI Date: Mon, 30 May 2022 17:02:15 +0900 Subject: [PATCH 3/9] added titlebar_color() --- egui_node_graph/src/editor_ui.rs | 8 ++++---- egui_node_graph/src/traits.rs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index ab37351..66c404b 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -323,15 +323,12 @@ where let mut responses = Vec::new(); let background_color; - let titlebar_color; let text_color; if ui.visuals().dark_mode { background_color = color_from_hex("#3f3f3f").unwrap(); - titlebar_color = background_color.lighten(0.8); text_color = color_from_hex("#fefefe").unwrap(); } else { background_color = color_from_hex("#ffffff").unwrap(); - titlebar_color = background_color.lighten(0.8); text_color = color_from_hex("#505050").unwrap(); } @@ -522,7 +519,10 @@ where let titlebar = Shape::Rect(RectShape { rect: titlebar_rect, rounding, - fill: titlebar_color, + fill: self.graph[self.node_id] + .user_data + .titlebar_color(ui, self.node_id, self.graph, user_state) + .unwrap_or(background_color.lighten(0.8)), stroke: Stroke::none(), }); diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 5be8908..a57c14e 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -43,6 +43,18 @@ where ) -> Vec> where Self::Response: UserResponseTrait; + + /// Set background color on titlebar + /// If the return value is None, the default color is set. + fn titlebar_color( + &self, + _ui: &egui::Ui, + _node_id: NodeId, + _graph: &Graph, + _user_state: &Self::UserState, + ) -> Option { + None + } } /// This trait can be implemented by any user type. The trait tells the library From 9f4d20e2e2b47ef1bc4d25f87e3a73fd1408fd61 Mon Sep 17 00:00:00 2001 From: KOKI Date: Mon, 30 May 2022 17:51:46 +0900 Subject: [PATCH 4/9] change connections color for each data type. --- egui_node_graph/src/editor_ui.rs | 26 +++++++++++++++----------- egui_node_graph/src/traits.rs | 4 ++-- egui_node_graph_example/src/app.rs | 4 ++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index ab37351..bdf2913 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -55,7 +55,7 @@ where ValueType: WidgetValueTrait, NodeTemplate: NodeTemplateTrait, - DataType: DataTypeTrait, + DataType: DataTypeTrait, { #[must_use] pub fn draw_graph_editor( @@ -150,13 +150,9 @@ where } /* Draw connections */ - - let connection_color = if ui.visuals().dark_mode { - color_from_hex("#efefef").unwrap() - } else { - color_from_hex("#bbbbbb").unwrap() - }; if let Some((_, ref locator)) = self.connection_in_progress { + let port_type = self.graph.any_param_type(*locator).unwrap(); + let connection_color = port_type.data_type_color(&self.user_state); let start_pos = port_locations[locator]; let (src_pos, dst_pos) = match locator { AnyParameterId::Output(_) => (start_pos, cursor_pos), @@ -166,6 +162,11 @@ where } for (input, output) in self.graph.iter_connections() { + let port_type = self + .graph + .any_param_type(AnyParameterId::Output(output)) + .unwrap(); + let connection_color = port_type.data_type_color(&self.user_state); let src_pos = port_locations[&AnyParameterId::Output(output)]; let dst_pos = port_locations[&AnyParameterId::Input(input)]; draw_connection(ui.painter(), src_pos, dst_pos, connection_color); @@ -298,7 +299,7 @@ where >, UserResponse: UserResponseTrait, ValueType: WidgetValueTrait, - DataType: DataTypeTrait, + DataType: DataTypeTrait, { pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0]; @@ -404,10 +405,11 @@ where let port_right = outer_rect.right(); #[allow(clippy::too_many_arguments)] - fn draw_port( + fn draw_port( ui: &mut Ui, graph: &Graph, node_id: NodeId, + user_state: &UserState, port_pos: Pos2, responses: &mut Vec>, param_id: AnyParameterId, @@ -415,7 +417,7 @@ where ongoing_drag: Option<(NodeId, AnyParameterId)>, is_connected_input: bool, ) where - DataType: DataTypeTrait, + DataType: DataTypeTrait, UserResponse: UserResponseTrait, { let port_type = graph.any_param_type(param_id).unwrap(); @@ -432,7 +434,7 @@ where let port_color = if resp.hovered() { Color32::WHITE } else { - port_type.data_type_color() + port_type.data_type_color(user_state) }; ui.painter() .circle(port_rect.center(), 5.0, port_color, Stroke::none()); @@ -478,6 +480,7 @@ where ui, self.graph, self.node_id, + user_state, pos_left, &mut responses, AnyParameterId::Input(*param), @@ -499,6 +502,7 @@ where ui, self.graph, self.node_id, + user_state, pos_right, &mut responses, AnyParameterId::Output(*param), diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 5be8908..4f6bf97 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -10,9 +10,9 @@ pub trait WidgetValueTrait { /// This trait must be implemented by the `DataType` generic parameter of the /// [`Graph`]. This trait tells the library how to visually expose data types /// to the user. -pub trait DataTypeTrait: PartialEq + Eq { +pub trait DataTypeTrait: PartialEq + Eq { // The associated port color of this datatype - fn data_type_color(&self) -> egui::Color32; + fn data_type_color(&self, user_state: &UserState) -> egui::Color32; // The name of this datatype fn name(&self) -> &str; diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index 6e0379a..7931a4b 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -89,8 +89,8 @@ pub struct MyGraphState { // =========== Then, you need to implement some traits ============ // A trait for the data types, to tell the library how to display them -impl DataTypeTrait for MyDataType { - fn data_type_color(&self) -> egui::Color32 { +impl DataTypeTrait for MyDataType { + fn data_type_color(&self, _user_state: &MyGraphState) -> egui::Color32 { match self { MyDataType::Scalar => egui::Color32::from_rgb(38, 109, 211), MyDataType::Vec2 => egui::Color32::from_rgb(238, 207, 109), From a9402c77e2ec1b851713224115285d51d734bdb7 Mon Sep 17 00:00:00 2001 From: KOKI Date: Mon, 30 May 2022 18:10:18 +0900 Subject: [PATCH 5/9] fix clippy --- egui_node_graph/src/editor_ui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index 66c404b..07988ae 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -522,7 +522,7 @@ where fill: self.graph[self.node_id] .user_data .titlebar_color(ui, self.node_id, self.graph, user_state) - .unwrap_or(background_color.lighten(0.8)), + .unwrap_or_else(|| background_color.lighten(0.8)), stroke: Stroke::none(), }); From ad3c9b13835711ff53c29ab303d93ff82f2607c4 Mon Sep 17 00:00:00 2001 From: IsseW Date: Fri, 27 May 2022 18:45:58 +0200 Subject: [PATCH 6/9] widget response --- egui_node_graph/src/editor_ui.rs | 12 +++++++++--- egui_node_graph/src/traits.rs | 3 ++- egui_node_graph_example/src/app.rs | 5 ++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index ab37351..c3f2fd4 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -52,7 +52,7 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: WidgetValueTrait, NodeTemplate: NodeTemplateTrait, DataType: DataTypeTrait, @@ -297,7 +297,7 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: WidgetValueTrait, DataType: DataTypeTrait, { pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0]; @@ -373,7 +373,13 @@ where if self.graph.connection(param_id).is_some() { ui.label(param_name); } else { - self.graph[param_id].value.value_widget(¶m_name, ui); + responses.extend( + self.graph[param_id] + .value + .value_widget(¶m_name, ui) + .into_iter() + .map(NodeResponse::User), + ); } let height_after = ui.min_rect().bottom(); input_port_heights.push((height_before + height_after) / 2.0); diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 5be8908..7665346 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -4,7 +4,8 @@ use super::*; /// [`Graph`]. The trait allows drawing custom inline widgets for the different /// types of the node graph. pub trait WidgetValueTrait { - fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui); + type Response; + fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec; } /// This trait must be implemented by the `DataType` generic parameter of the diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index 6e0379a..147b7ca 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -250,7 +250,8 @@ impl NodeTemplateIter for AllMyNodeTemplates { } impl WidgetValueTrait for MyValueType { - fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) { + type Response = MyResponse; + fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec { // This trait is used to tell the library which UI to display for the // inline parameter widgets. match self { @@ -270,6 +271,8 @@ impl WidgetValueTrait for MyValueType { }); } } + // This allows you to retorn your responses from the inline widgets. + Vec::new() } } From 6174e14a0f3c287e7ea997afb357a2d5404f3591 Mon Sep 17 00:00:00 2001 From: IsseW Date: Tue, 31 May 2022 11:03:13 +0200 Subject: [PATCH 7/9] add doc comment, and fix typo --- egui_node_graph/src/traits.rs | 4 ++++ egui_node_graph_example/src/app.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 7665346..376d870 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -5,6 +5,10 @@ use super::*; /// types of the node graph. pub trait WidgetValueTrait { type Response; + /// This method will be called for each input parameter with a widget. The + /// return value is a vector of custom response objects which can be used + /// to implement handling of side effects. If unsure, the response Vec can + /// be empty. fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec; } diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index 147b7ca..1c2c812 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -271,7 +271,7 @@ impl WidgetValueTrait for MyValueType { }); } } - // This allows you to retorn your responses from the inline widgets. + // This allows you to return your responses from the inline widgets. Vec::new() } } From 7204ac79c5c6e05fd0e121c96c459d59ecd8743d Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Tue, 7 Jun 2022 21:44:51 +0200 Subject: [PATCH 8/9] Address review comments --- egui_node_graph/src/editor_ui.rs | 10 ++++++-- egui_node_graph/src/graph_impls.rs | 38 +++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index 4896b04..03bb537 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -203,8 +203,14 @@ where self.selected_node = Some(node_id); } NodeResponse::DeleteNode(node_id) => { - let (ins, _) = self.graph.remove_node(node_id); - extra_responses.extend(ins.into_iter().map(NodeResponse::DisconnectEvent)); + let removed = self.graph.remove_node(node_id); + extra_responses.extend( + removed + .into_iter() + .map(|x| x.0) + .into_iter() + .map(NodeResponse::DisconnectEvent), + ); self.node_positions.remove(node_id); // Make sure to not leave references to old nodes hanging if self.selected_node.map(|x| x == node_id).unwrap_or(false) { diff --git a/egui_node_graph/src/graph_impls.rs b/egui_node_graph/src/graph_impls.rs index 42cd96e..9c26881 100644 --- a/egui_node_graph/src/graph_impls.rs +++ b/egui_node_graph/src/graph_impls.rs @@ -63,20 +63,36 @@ impl Graph { output_id } - /// Returns the list of input and output ids that were disconnected. - pub fn remove_node(&mut self, node_id: NodeId) -> (SVec, SVec) { - self.connections - .retain(|i, o| !(self.outputs[*o].node == node_id || self.inputs[i].node == node_id)); - let inputs: SVec<_> = self[node_id].input_ids().collect(); - for input in &inputs { - self.inputs.remove(*input); + /// Removes a node from the graph with given `node_id`. This also removes + /// any incoming or outgoing connections from that node + /// + /// This function returns the list of connections that has been removed + /// 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 + /// calling this function. + pub fn remove_node(&mut self, node_id: NodeId) -> Vec<(InputId, OutputId)> { + let mut disconnect_events = vec![]; + + self.connections.retain(|i, o| { + if self.outputs[*o].node == node_id || self.inputs[i].node == node_id { + disconnect_events.push((i, *o)); + false + } else { + true + } + }); + + // NOTE: Collect is needed because we can't borrow the input ids while + // we remove them inside the loop. + for input in self[node_id].input_ids().collect::>() { + self.inputs.remove(input); } - let outputs: SVec<_> = self[node_id].output_ids().collect(); - for output in &outputs { - self.outputs.remove(*output); + for output in self[node_id].output_ids().collect::>() { + self.outputs.remove(output); } self.nodes.remove(node_id); - (inputs, outputs) + + disconnect_events } pub fn remove_connection(&mut self, input_id: InputId) -> Option { From d8189997ce1810b8a413adce244ef6f06c609e1b Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Thu, 9 Jun 2022 21:25:25 +0200 Subject: [PATCH 9/9] Multiple improvements --- egui_node_graph/src/editor_ui.rs | 66 +++++++++++++++--------------- egui_node_graph/src/traits.rs | 33 +++++++++++++-- egui_node_graph_example/src/app.rs | 10 ++--- 3 files changed, 67 insertions(+), 42 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index de015d5..15618bd 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -15,11 +15,17 @@ pub type PortLocations = std::collections::HashMap; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum NodeResponse { ConnectEventStarted(NodeId, AnyParameterId), - ConnectEventEnded(AnyParameterId), + ConnectEventEnded { + output: OutputId, + input: InputId, + }, CreatedNode(NodeId), SelectNode(NodeId), DeleteNode(NodeId), - DisconnectEvent(InputId), + DisconnectEvent { + output: OutputId, + input: InputId, + }, /// Emitted when a node is interacted with, and should be raised RaiseNode(NodeId), User(UserResponse), @@ -183,24 +189,8 @@ where NodeResponse::ConnectEventStarted(node_id, port) => { self.connection_in_progress = Some((node_id, port)); } - NodeResponse::ConnectEventEnded(locator) => { - let in_out = match ( - self.connection_in_progress - .map(|(_node, param)| param) - .take() - .expect("Cannot end drag without in-progress connection."), - locator, - ) { - (AnyParameterId::Input(input), AnyParameterId::Output(output)) - | (AnyParameterId::Output(output), AnyParameterId::Input(input)) => { - Some((input, output)) - } - _ => None, - }; - - if let Some((input, output)) = in_out { - self.graph.add_connection(output, input) - } + NodeResponse::ConnectEventEnded { input, output } => { + self.graph.add_connection(output, input) } NodeResponse::CreatedNode(_) => { //Convenience NodeResponse for users @@ -213,9 +203,7 @@ where extra_responses.extend( removed .into_iter() - .map(|x| x.0) - .into_iter() - .map(NodeResponse::DisconnectEvent), + .map(|(input, output)| NodeResponse::DisconnectEvent { input, output }), ); self.node_positions.remove(node_id); // Make sure to not leave references to old nodes hanging @@ -224,15 +212,11 @@ where } self.node_order.retain(|id| *id != node_id); } - NodeResponse::DisconnectEvent(input_id) => { - let corresp_output = self - .graph - .connection(input_id) - .expect("Connection data should be valid"); - let other_node = self.graph.get_input(input_id).node(); - self.graph.remove_connection(input_id); + NodeResponse::DisconnectEvent { input, output } => { + let other_node = self.graph.get_input(input).node(); + self.graph.remove_connection(input); self.connection_in_progress = - Some((other_node, AnyParameterId::Output(corresp_output))); + Some((other_node, AnyParameterId::Output(output))); } NodeResponse::RaiseNode(node_id) => { let old_pos = self @@ -375,6 +359,7 @@ where .text_style(TextStyle::Button) .color(text_color), )); + ui.add_space(8.0); // The size of the little cross icon }); ui.add_space(margin.y); title_height = ui.min_size().y; @@ -460,7 +445,14 @@ where if resp.drag_started() { if is_connected_input { - responses.push(NodeResponse::DisconnectEvent(param_id.assume_input())); + let input = param_id.assume_input(); + let corresp_output = graph + .connection(input) + .expect("Connection data should be valid"); + responses.push(NodeResponse::DisconnectEvent { + input: param_id.assume_input(), + output: corresp_output, + }); } else { responses.push(NodeResponse::ConnectEventStarted(node_id, param_id)); } @@ -473,7 +465,13 @@ where && resp.hovered() && ui.input().pointer.any_released() { - responses.push(NodeResponse::ConnectEventEnded(param_id)); + match (param_id, origin_param) { + (AnyParameterId::Input(input), AnyParameterId::Output(output)) + | (AnyParameterId::Output(output), AnyParameterId::Input(input)) => { + responses.push(NodeResponse::ConnectEventEnded { input, output }); + } + _ => { /* Ignore in-in or out-out connections */ } + } } } } @@ -532,7 +530,7 @@ where } // Draw the background shape. - // NOTE: This code is a bit more involve than it needs to be because egui + // NOTE: This code is a bit more involved than it needs to be because egui // does not support drawing rectangles with asymmetrical round corners. let (shape, outline) = { diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 32acae1..2f5c6d6 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -16,11 +16,38 @@ pub trait WidgetValueTrait { /// [`Graph`]. This trait tells the library how to visually expose data types /// to the user. pub trait DataTypeTrait: PartialEq + Eq { - // The associated port color of this datatype + /// The associated port color of this datatype fn data_type_color(&self, user_state: &UserState) -> egui::Color32; - // The name of this datatype - fn name(&self) -> &str; + /// The name of this datatype. Return type is specified as Cow because + /// some implementations will need to allocate a new string to provide an + /// answer while others won't. + /// + /// ## Example (borrowed value) + /// Use this when you can get the name of the datatype from its fields or as + /// a &'static str. Prefer this method when possible. + /// ```rust + /// pub struct DataType { name: String } + /// + /// impl DataTypeTrait<()> for DataType { + /// fn name(&self) -> std::borrow::Cow { + /// Cow::Borrowed(&self.name) + /// } + /// } + /// ``` + /// + /// ## Example (owned value) + /// Use this when you can't derive the name of the datatype from its fields. + /// ```rust + /// pub struct DataType { some_tag: i32 } + /// + /// impl DataTypeTrait<()> for DataType { + /// fn name(&self) -> std::borrow::Cow { + /// Cow::Owned(format!("Super amazing type #{}", self.some_tag)) + /// } + /// } + /// ``` + fn name(&self) -> std::borrow::Cow; } /// This trait must be implemented for the `NodeData` generic parameter of the diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index 563751c..e8e47dc 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; use eframe::egui::{self, DragValue, TextStyle}; use egui_node_graph::*; @@ -97,10 +97,10 @@ impl DataTypeTrait for MyDataType { } } - fn name(&self) -> &str { + fn name(&self) -> Cow<'_, str> { match self { - MyDataType::Scalar => "scalar", - MyDataType::Vec2 => "2d vector", + MyDataType::Scalar => Cow::Borrowed("scalar"), + MyDataType::Vec2 => Cow::Borrowed("2d vector"), } } } @@ -383,7 +383,7 @@ impl eframe::App for NodeGraphExample { Err(err) => format!("Execution error: {}", err), }; ctx.debug_painter().text( - egui::pos2(10.0, 10.0), + egui::pos2(10.0, 35.0), egui::Align2::LEFT_TOP, text, TextStyle::Button.resolve(&ctx.style()),