Merge pull request #69 from setzer22/feature/user_state_in_value_widget

Expose UserState and the node id to value_widget
This commit is contained in:
setzer22 2022-11-12 15:28:59 +01:00 committed by GitHub
commit c2310e5f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 26 deletions

View File

@ -76,7 +76,8 @@ where
ValueType = ValueType, ValueType = ValueType,
>, >,
UserResponse: UserResponseTrait, UserResponse: UserResponseTrait,
ValueType: WidgetValueTrait<Response = UserResponse>, ValueType:
WidgetValueTrait<Response = UserResponse, UserState = UserState, NodeData = NodeData>,
NodeTemplate: NodeTemplateTrait< NodeTemplate: NodeTemplateTrait<
NodeData = NodeData, NodeData = NodeData,
DataType = DataType, DataType = DataType,
@ -152,10 +153,10 @@ where
node_finder_area = node_finder_area.current_pos(pos); node_finder_area = node_finder_area.current_pos(pos);
} }
node_finder_area.show(ui.ctx(), |ui| { 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( let new_node = self.graph.add_node(
node_kind.node_graph_label(), node_kind.node_graph_label(user_state),
node_kind.user_data(), node_kind.user_data(user_state),
|graph, node_id| node_kind.build_node(graph, user_state, node_id), |graph, node_id| node_kind.build_node(graph, user_state, node_id),
); );
self.node_positions.insert( self.node_positions.insert(
@ -391,7 +392,8 @@ where
ValueType = ValueType, ValueType = ValueType,
>, >,
UserResponse: UserResponseTrait, UserResponse: UserResponseTrait,
ValueType: WidgetValueTrait<Response = UserResponse>, ValueType:
WidgetValueTrait<Response = UserResponse, UserState = UserState, NodeData = NodeData>,
DataType: DataTypeTrait<UserState>, DataType: DataTypeTrait<UserState>,
{ {
pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0]; pub const MAX_NODE_SIZE: [f32; 2] = [200.0, 200.0];
@ -469,13 +471,23 @@ where
if self.graph.connection(param_id).is_some() { if self.graph.connection(param_id).is_some() {
ui.label(param_name); ui.label(param_name);
} else { } else {
responses.extend( // NOTE: We want to pass the `user_data` to
self.graph[param_id] // `value_widget`, but we can't since that would require
.value // borrowing the graph twice. Here, we make the
.value_widget(&param_name, ui) // assumption that the value is cheaply replaced, and
.into_iter() // use `std::mem::take` to temporarily replace it with a
.map(NodeResponse::User), // 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(
&param_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(); let height_after = ui.min_rect().bottom();
input_port_heights.push((height_before + height_after) / 2.0); input_port_heights.push((height_before + height_after) / 2.0);

View File

@ -14,9 +14,9 @@ pub struct NodeFinder<NodeTemplate> {
_phantom: PhantomData<NodeTemplate>, _phantom: PhantomData<NodeTemplate>,
} }
impl<NodeTemplate, NodeData> NodeFinder<NodeTemplate> impl<NodeTemplate, NodeData, UserState> NodeFinder<NodeTemplate>
where where
NodeTemplate: NodeTemplateTrait<NodeData = NodeData>, NodeTemplate: NodeTemplateTrait<NodeData = NodeData, UserState = UserState>,
{ {
pub fn new_at(pos: Pos2) -> Self { pub fn new_at(pos: Pos2) -> Self {
NodeFinder { NodeFinder {
@ -34,6 +34,7 @@ where
&mut self, &mut self,
ui: &mut Ui, ui: &mut Ui,
all_kinds: impl NodeTemplateIter<Item = NodeTemplate>, all_kinds: impl NodeTemplateIter<Item = NodeTemplate>,
user_state: &mut UserState,
) -> Option<NodeTemplate> { ) -> Option<NodeTemplate> {
let background_color; let background_color;
let text_color; let text_color;
@ -68,7 +69,7 @@ where
.inner_margin(vec2(10.0, 10.0)) .inner_margin(vec2(10.0, 10.0))
.show(ui, |ui| { .show(ui, |ui| {
for kind in all_kinds.all_kinds() { 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 if kind_name
.to_lowercase() .to_lowercase()
.contains(self.query.to_lowercase().as_str()) .contains(self.query.to_lowercase().as_str())

View File

@ -3,13 +3,28 @@ use super::*;
/// This trait must be implemented by the `ValueType` generic parameter of the /// This trait must be implemented by the `ValueType` generic parameter of the
/// [`Graph`]. The trait allows drawing custom inline widgets for the different /// [`Graph`]. The trait allows drawing custom inline widgets for the different
/// types of the node graph. /// types of the node graph.
pub trait WidgetValueTrait { ///
/// 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 Response;
type UserState;
type NodeData;
/// This method will be called for each input parameter with a widget. The /// 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 /// 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 /// to implement handling of side effects. If unsure, the response Vec can
/// be empty. /// be empty.
fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec<Self::Response>; fn value_widget(
&mut self,
param_name: &str,
node_id: NodeId,
ui: &mut egui::Ui,
user_state: &mut Self::UserState,
node_data: &Self::NodeData,
) -> Vec<Self::Response>;
} }
/// This trait must be implemented by the `DataType` generic parameter of the /// This trait must be implemented by the `DataType` generic parameter of the
@ -112,13 +127,17 @@ pub trait NodeTemplateTrait: Clone {
type UserState; type UserState;
/// Returns a descriptive name for the node kind, used in the node finder. /// Returns a descriptive name for the node kind, used in the node finder.
fn node_finder_label(&self) -> &str; ///
/// The return type is Cow<str> 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<str>;
/// Returns a descriptive name for the node kind, used in the graph. /// 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. /// 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 /// 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 /// node will be empty by default, and this function can be used to fill its

View File

@ -37,6 +37,14 @@ pub enum MyValueType {
Scalar { value: f32 }, 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 { impl MyValueType {
/// Tries to downcast this value type to a vector /// Tries to downcast this value type to a vector
pub fn try_to_vec2(self) -> anyhow::Result<egui::Vec2> { pub fn try_to_vec2(self) -> anyhow::Result<egui::Vec2> {
@ -118,8 +126,8 @@ impl NodeTemplateTrait for MyNodeTemplate {
type ValueType = MyValueType; type ValueType = MyValueType;
type UserState = MyGraphState; type UserState = MyGraphState;
fn node_finder_label(&self) -> &str { fn node_finder_label(&self, _user_state: &mut Self::UserState) -> Cow<'_, str> {
match self { Cow::Borrowed(match self {
MyNodeTemplate::MakeVector => "New vector", MyNodeTemplate::MakeVector => "New vector",
MyNodeTemplate::MakeScalar => "New scalar", MyNodeTemplate::MakeScalar => "New scalar",
MyNodeTemplate::AddScalar => "Scalar add", MyNodeTemplate::AddScalar => "Scalar add",
@ -127,16 +135,16 @@ impl NodeTemplateTrait for MyNodeTemplate {
MyNodeTemplate::AddVector => "Vector add", MyNodeTemplate::AddVector => "Vector add",
MyNodeTemplate::SubtractVector => "Vector subtract", MyNodeTemplate::SubtractVector => "Vector subtract",
MyNodeTemplate::VectorTimesScalar => "Vector times scalar", MyNodeTemplate::VectorTimesScalar => "Vector times scalar",
} })
} }
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 // 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. // 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 } MyNodeData { template: *self }
} }
@ -258,7 +266,16 @@ impl NodeTemplateIter for AllMyNodeTemplates {
impl WidgetValueTrait for MyValueType { impl WidgetValueTrait for MyValueType {
type Response = MyResponse; type Response = MyResponse;
fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui) -> Vec<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<MyResponse> {
// This trait is used to tell the library which UI to display for the // This trait is used to tell the library which UI to display for the
// inline parameter widgets. // inline parameter widgets.
match self { match self {