From 79fbe314826573807fbcd35b2c7c982bfb1c75b6 Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Sat, 29 Oct 2022 15:43:55 +0200 Subject: [PATCH 1/5] Expose UserState and the node id to value_widget --- egui_node_graph/src/editor_ui.rs | 6 +++--- egui_node_graph/src/traits.rs | 9 ++++++++- egui_node_graph_example/src/app.rs | 9 ++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index e168c34..06504de 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -76,7 +76,7 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: WidgetValueTrait, NodeTemplate: NodeTemplateTrait< NodeData = NodeData, DataType = DataType, @@ -391,7 +391,7 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: WidgetValueTrait, DataType: DataTypeTrait, { pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0]; @@ -472,7 +472,7 @@ where responses.extend( self.graph[param_id] .value - .value_widget(¶m_name, ui) + .value_widget(¶m_name, self.node_id, ui, user_state) .into_iter() .map(NodeResponse::User), ); diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 8b90191..bc51f5f 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -5,11 +5,18 @@ use super::*; /// types of the node graph. pub trait WidgetValueTrait { type Response; + type UserState; /// 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; + fn value_widget( + &mut self, + param_name: &str, + node_id: NodeId, + ui: &mut egui::Ui, + user_state: &mut Self::UserState, + ) -> 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 0bad5c9..47715bb 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -258,7 +258,14 @@ impl NodeTemplateIter for AllMyNodeTemplates { impl WidgetValueTrait for MyValueType { type Response = MyResponse; - fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec { + type UserState = MyGraphState; + fn value_widget( + &mut self, + param_name: &str, + _node_id: NodeId, + ui: &mut egui::Ui, + _user_state: &mut MyGraphState, + ) -> Vec { // This trait is used to tell the library which UI to display for the // inline parameter widgets. match self { From 0ab08bd9cad77756a581d17ae1307d36ad9eb7ef Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Sat, 29 Oct 2022 17:22:14 +0200 Subject: [PATCH 2/5] Also expose UserState in NodeTemplateTrait --- egui_node_graph/src/editor_ui.rs | 6 +++--- egui_node_graph/src/node_finder.rs | 7 ++++--- egui_node_graph/src/traits.rs | 6 +++--- egui_node_graph_example/src/app.rs | 8 ++++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index 06504de..7bd55cb 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -152,10 +152,10 @@ where node_finder_area = node_finder_area.current_pos(pos); } node_finder_area.show(ui.ctx(), |ui| { - if let Some(node_kind) = node_finder.show(ui, all_kinds) { + if let Some(node_kind) = node_finder.show(ui, all_kinds, user_state) { let new_node = self.graph.add_node( - node_kind.node_graph_label(), - node_kind.user_data(), + node_kind.node_graph_label(user_state), + node_kind.user_data(user_state), |graph, node_id| node_kind.build_node(graph, user_state, node_id), ); self.node_positions.insert( diff --git a/egui_node_graph/src/node_finder.rs b/egui_node_graph/src/node_finder.rs index e445346..6bb77d7 100644 --- a/egui_node_graph/src/node_finder.rs +++ b/egui_node_graph/src/node_finder.rs @@ -14,9 +14,9 @@ pub struct NodeFinder { _phantom: PhantomData, } -impl NodeFinder +impl NodeFinder where - NodeTemplate: NodeTemplateTrait, + NodeTemplate: NodeTemplateTrait, { pub fn new_at(pos: Pos2) -> Self { NodeFinder { @@ -34,6 +34,7 @@ where &mut self, ui: &mut Ui, all_kinds: impl NodeTemplateIter, + user_state: &mut UserState, ) -> Option { let background_color; let text_color; @@ -68,7 +69,7 @@ where .inner_margin(vec2(10.0, 10.0)) .show(ui, |ui| { for kind in all_kinds.all_kinds() { - let kind_name = kind.node_finder_label().to_string(); + let kind_name = kind.node_finder_label(user_state).to_string(); if kind_name .to_lowercase() .contains(self.query.to_lowercase().as_str()) diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index bc51f5f..67e261b 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -119,13 +119,13 @@ pub trait NodeTemplateTrait: Clone { type UserState; /// Returns a descriptive name for the node kind, used in the node finder. - fn node_finder_label(&self) -> &str; + fn node_finder_label(&self, user_state: &mut Self::UserState) -> &str; /// Returns a descriptive name for the node kind, used in the graph. - fn node_graph_label(&self) -> String; + fn node_graph_label(&self, user_state: &mut Self::UserState) -> String; /// Returns the user data for this node kind. - fn user_data(&self) -> Self::NodeData; + fn user_data(&self, user_state: &mut Self::UserState) -> Self::NodeData; /// This function is run when this node kind gets added to the graph. The /// node will be empty by default, and this function can be used to fill its diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index 47715bb..e2df3e7 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -118,7 +118,7 @@ impl NodeTemplateTrait for MyNodeTemplate { type ValueType = MyValueType; type UserState = MyGraphState; - fn node_finder_label(&self) -> &str { + fn node_finder_label(&self, _user_state: &mut Self::UserState) -> &str { match self { MyNodeTemplate::MakeVector => "New vector", MyNodeTemplate::MakeScalar => "New scalar", @@ -130,13 +130,13 @@ impl NodeTemplateTrait for MyNodeTemplate { } } - fn node_graph_label(&self) -> String { + fn node_graph_label(&self, user_state: &mut Self::UserState) -> String { // It's okay to delegate this to node_finder_label if you don't want to // show different names in the node finder and the node itself. - self.node_finder_label().into() + self.node_finder_label(user_state).into() } - fn user_data(&self) -> Self::NodeData { + fn user_data(&self, _user_state: &mut Self::UserState) -> Self::NodeData { MyNodeData { template: *self } } From d6c1b324eed59ddef706d9010131b4bb6561f681 Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Sat, 29 Oct 2022 18:22:50 +0200 Subject: [PATCH 3/5] Also add NodeData parameter to value_widget --- egui_node_graph/src/editor_ui.rs | 28 ++++++++++++++++++++-------- egui_node_graph/src/traits.rs | 10 ++++++++-- egui_node_graph_example/src/app.rs | 16 +++++++++++++--- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index 7bd55cb..f630b2a 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -76,7 +76,8 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: + WidgetValueTrait, NodeTemplate: NodeTemplateTrait< NodeData = NodeData, DataType = DataType, @@ -391,7 +392,8 @@ where ValueType = ValueType, >, UserResponse: UserResponseTrait, - ValueType: WidgetValueTrait, + ValueType: + WidgetValueTrait, DataType: DataTypeTrait, { pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0]; @@ -469,13 +471,23 @@ where if self.graph.connection(param_id).is_some() { ui.label(param_name); } else { - responses.extend( - self.graph[param_id] - .value - .value_widget(¶m_name, self.node_id, ui, user_state) - .into_iter() - .map(NodeResponse::User), + // NOTE: We want to pass the `user_data` to + // `value_widget`, but we can't since that would require + // borrowing the graph twice. Here, we take the + // assumption that the value is cheaply replace, and use + // `std::mem::take` to temporarily replace it with a + // dummy value. This requires `ValueType` to implement + // Default, but results in a totally safe alternative. + let mut value = std::mem::take(&mut self.graph[param_id].value); + let node_responses = value.value_widget( + ¶m_name, + self.node_id, + ui, + user_state, + &self.graph[self.node_id].user_data, ); + self.graph[param_id].value = value; + responses.extend(node_responses.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 67e261b..fe7a46c 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -3,9 +3,10 @@ use super::*; /// This trait must be implemented by the `ValueType` generic parameter of the /// [`Graph`]. The trait allows drawing custom inline widgets for the different /// types of the node graph. -pub trait WidgetValueTrait { +pub trait WidgetValueTrait : Default { type Response; type UserState; + type NodeData; /// 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 @@ -16,6 +17,7 @@ pub trait WidgetValueTrait { node_id: NodeId, ui: &mut egui::Ui, user_state: &mut Self::UserState, + node_data: &Self::NodeData, ) -> Vec; } @@ -119,7 +121,11 @@ pub trait NodeTemplateTrait: Clone { type UserState; /// Returns a descriptive name for the node kind, used in the node finder. - fn node_finder_label(&self, user_state: &mut Self::UserState) -> &str; + /// + /// The return type is Cow to allow returning owned or borrowed values + /// more flexibly. Refer to the documentation for `DataTypeTrait::name` for + /// more information + fn node_finder_label(&self, user_state: &mut Self::UserState) -> std::borrow::Cow; /// Returns a descriptive name for the node kind, used in the graph. fn node_graph_label(&self, user_state: &mut Self::UserState) -> String; diff --git a/egui_node_graph_example/src/app.rs b/egui_node_graph_example/src/app.rs index e2df3e7..5ad68ca 100644 --- a/egui_node_graph_example/src/app.rs +++ b/egui_node_graph_example/src/app.rs @@ -37,6 +37,14 @@ pub enum MyValueType { Scalar { value: f32 }, } +impl Default for MyValueType { + fn default() -> Self { + // NOTE: This is just a dummy `Default` implementation. The library + // requires it to circumvent some internal borrow checker issues. + Self::Scalar { value: 0.0 } + } +} + impl MyValueType { /// Tries to downcast this value type to a vector pub fn try_to_vec2(self) -> anyhow::Result { @@ -118,8 +126,8 @@ impl NodeTemplateTrait for MyNodeTemplate { type ValueType = MyValueType; type UserState = MyGraphState; - fn node_finder_label(&self, _user_state: &mut Self::UserState) -> &str { - match self { + fn node_finder_label(&self, _user_state: &mut Self::UserState) -> Cow<'_, str> { + Cow::Borrowed(match self { MyNodeTemplate::MakeVector => "New vector", MyNodeTemplate::MakeScalar => "New scalar", MyNodeTemplate::AddScalar => "Scalar add", @@ -127,7 +135,7 @@ impl NodeTemplateTrait for MyNodeTemplate { MyNodeTemplate::AddVector => "Vector add", MyNodeTemplate::SubtractVector => "Vector subtract", MyNodeTemplate::VectorTimesScalar => "Vector times scalar", - } + }) } fn node_graph_label(&self, user_state: &mut Self::UserState) -> String { @@ -259,12 +267,14 @@ impl NodeTemplateIter for AllMyNodeTemplates { impl WidgetValueTrait for MyValueType { type Response = MyResponse; type UserState = MyGraphState; + type NodeData = MyNodeData; fn value_widget( &mut self, param_name: &str, _node_id: NodeId, ui: &mut egui::Ui, _user_state: &mut MyGraphState, + _node_data: &MyNodeData, ) -> Vec { // This trait is used to tell the library which UI to display for the // inline parameter widgets. From 3fd60c532b13aa575faf836b2eb37c96a16927a7 Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Sat, 29 Oct 2022 18:29:57 +0200 Subject: [PATCH 4/5] Better documentation for the new `Default` trait bound --- egui_node_graph/src/editor_ui.rs | 6 +++--- egui_node_graph/src/traits.rs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/egui_node_graph/src/editor_ui.rs b/egui_node_graph/src/editor_ui.rs index f630b2a..56ff218 100644 --- a/egui_node_graph/src/editor_ui.rs +++ b/egui_node_graph/src/editor_ui.rs @@ -473,9 +473,9 @@ where } else { // NOTE: We want to pass the `user_data` to // `value_widget`, but we can't since that would require - // borrowing the graph twice. Here, we take the - // assumption that the value is cheaply replace, and use - // `std::mem::take` to temporarily replace it with a + // borrowing the graph twice. Here, we make the + // assumption that the value is cheaply replaced, and + // use `std::mem::take` to temporarily replace it with a // dummy value. This requires `ValueType` to implement // Default, but results in a totally safe alternative. let mut value = std::mem::take(&mut self.graph[param_id].value); diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index fe7a46c..638b590 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -3,6 +3,12 @@ use super::*; /// This trait must be implemented by the `ValueType` generic parameter of the /// [`Graph`]. The trait allows drawing custom inline widgets for the different /// types of the node graph. +/// +/// The [`Default`] trait bound is required to circumvent borrow checker issues +/// using `std::mem::take` Otherwise, it would be impossible to pass the +/// `node_data` parameter during `value_widget`. The default value is never +/// used, so the implementation is not important, but it should be reasonably +/// cheap to construct. pub trait WidgetValueTrait : Default { type Response; type UserState; From 8706908d13b8a42e22784afe17fbc3a2ead13047 Mon Sep 17 00:00:00 2001 From: Setzer22 Date: Sun, 6 Nov 2022 09:18:21 +0100 Subject: [PATCH 5/5] fmt --- egui_node_graph/src/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/egui_node_graph/src/traits.rs b/egui_node_graph/src/traits.rs index 638b590..6f02320 100644 --- a/egui_node_graph/src/traits.rs +++ b/egui_node_graph/src/traits.rs @@ -9,7 +9,7 @@ use super::*; /// `node_data` parameter during `value_widget`. The default value is never /// used, so the implementation is not important, but it should be reasonably /// cheap to construct. -pub trait WidgetValueTrait : Default { +pub trait WidgetValueTrait: Default { type Response; type UserState; type NodeData;