mirror of
https://github.com/eliasstepanik/egui_node_graph.git
synced 2026-01-11 22:08:28 +00:00
Merge branch 'main' into fkaa/fix-small-stuff
This commit is contained in:
commit
a00b3c6ef2
@ -15,11 +15,17 @@ pub type PortLocations = std::collections::HashMap<AnyParameterId, Pos2>;
|
|||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum NodeResponse<UserResponse: UserResponseTrait> {
|
pub enum NodeResponse<UserResponse: UserResponseTrait> {
|
||||||
ConnectEventStarted(NodeId, AnyParameterId),
|
ConnectEventStarted(NodeId, AnyParameterId),
|
||||||
ConnectEventEnded(AnyParameterId),
|
ConnectEventEnded {
|
||||||
|
output: OutputId,
|
||||||
|
input: InputId,
|
||||||
|
},
|
||||||
CreatedNode(NodeId),
|
CreatedNode(NodeId),
|
||||||
SelectNode(NodeId),
|
SelectNode(NodeId),
|
||||||
DeleteNode(NodeId),
|
DeleteNode(NodeId),
|
||||||
DisconnectEvent(InputId),
|
DisconnectEvent {
|
||||||
|
output: OutputId,
|
||||||
|
input: InputId,
|
||||||
|
},
|
||||||
/// Emitted when a node is interacted with, and should be raised
|
/// Emitted when a node is interacted with, and should be raised
|
||||||
RaiseNode(NodeId),
|
RaiseNode(NodeId),
|
||||||
User(UserResponse),
|
User(UserResponse),
|
||||||
@ -52,10 +58,10 @@ where
|
|||||||
ValueType = ValueType,
|
ValueType = ValueType,
|
||||||
>,
|
>,
|
||||||
UserResponse: UserResponseTrait,
|
UserResponse: UserResponseTrait,
|
||||||
ValueType: WidgetValueTrait,
|
ValueType: WidgetValueTrait<Response = UserResponse>,
|
||||||
NodeTemplate:
|
NodeTemplate:
|
||||||
NodeTemplateTrait<NodeData = NodeData, DataType = DataType, ValueType = ValueType>,
|
NodeTemplateTrait<NodeData = NodeData, DataType = DataType, ValueType = ValueType>,
|
||||||
DataType: DataTypeTrait,
|
DataType: DataTypeTrait<UserState>,
|
||||||
{
|
{
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn draw_graph_editor(
|
pub fn draw_graph_editor(
|
||||||
@ -152,13 +158,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Draw connections */
|
/* 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 {
|
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 start_pos = port_locations[locator];
|
||||||
let (src_pos, dst_pos) = match locator {
|
let (src_pos, dst_pos) = match locator {
|
||||||
AnyParameterId::Output(_) => (start_pos, cursor_pos),
|
AnyParameterId::Output(_) => (start_pos, cursor_pos),
|
||||||
@ -168,6 +170,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (input, output) in self.graph.iter_connections() {
|
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 src_pos = port_locations[&AnyParameterId::Output(output)];
|
||||||
let dst_pos = port_locations[&AnyParameterId::Input(input)];
|
let dst_pos = port_locations[&AnyParameterId::Input(input)];
|
||||||
draw_connection(ui.painter(), src_pos, dst_pos, connection_color);
|
draw_connection(ui.painter(), src_pos, dst_pos, connection_color);
|
||||||
@ -175,29 +182,17 @@ where
|
|||||||
|
|
||||||
/* Handle responses from drawing nodes */
|
/* 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<NodeResponse<UserResponse>> = Vec::new();
|
||||||
|
|
||||||
for response in delayed_responses.iter().copied() {
|
for response in delayed_responses.iter().copied() {
|
||||||
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(locator) => {
|
NodeResponse::ConnectEventEnded { input, output } => {
|
||||||
let in_out = match (
|
self.graph.add_connection(output, input)
|
||||||
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::CreatedNode(_) => {
|
NodeResponse::CreatedNode(_) => {
|
||||||
//Convenience NodeResponse for users
|
//Convenience NodeResponse for users
|
||||||
@ -206,7 +201,12 @@ where
|
|||||||
self.selected_node = Some(node_id);
|
self.selected_node = Some(node_id);
|
||||||
}
|
}
|
||||||
NodeResponse::DeleteNode(node_id) => {
|
NodeResponse::DeleteNode(node_id) => {
|
||||||
self.graph.remove_node(node_id);
|
let removed = self.graph.remove_node(node_id);
|
||||||
|
extra_responses.extend(
|
||||||
|
removed
|
||||||
|
.into_iter()
|
||||||
|
.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) {
|
||||||
@ -214,15 +214,11 @@ where
|
|||||||
}
|
}
|
||||||
self.node_order.retain(|id| *id != node_id);
|
self.node_order.retain(|id| *id != node_id);
|
||||||
}
|
}
|
||||||
NodeResponse::DisconnectEvent(input_id) => {
|
NodeResponse::DisconnectEvent { input, output } => {
|
||||||
let corresp_output = self
|
let other_node = self.graph.get_input(input).node();
|
||||||
.graph
|
self.graph.remove_connection(input);
|
||||||
.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);
|
|
||||||
self.connection_in_progress =
|
self.connection_in_progress =
|
||||||
Some((other_node, AnyParameterId::Output(corresp_output)));
|
Some((other_node, AnyParameterId::Output(output)));
|
||||||
}
|
}
|
||||||
NodeResponse::RaiseNode(node_id) => {
|
NodeResponse::RaiseNode(node_id) => {
|
||||||
let old_pos = self
|
let old_pos = self
|
||||||
@ -239,6 +235,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 */
|
/* Mouse input handling */
|
||||||
|
|
||||||
// This locks the context, so don't hold on to it for too long.
|
// This locks the context, so don't hold on to it for too long.
|
||||||
@ -299,8 +300,8 @@ where
|
|||||||
ValueType = ValueType,
|
ValueType = ValueType,
|
||||||
>,
|
>,
|
||||||
UserResponse: UserResponseTrait,
|
UserResponse: UserResponseTrait,
|
||||||
ValueType: WidgetValueTrait,
|
ValueType: WidgetValueTrait<Response = UserResponse>,
|
||||||
DataType: DataTypeTrait,
|
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];
|
||||||
|
|
||||||
@ -325,15 +326,12 @@ where
|
|||||||
let mut responses = Vec::new();
|
let mut responses = Vec::new();
|
||||||
|
|
||||||
let background_color;
|
let background_color;
|
||||||
let titlebar_color;
|
|
||||||
let text_color;
|
let text_color;
|
||||||
if ui.visuals().dark_mode {
|
if ui.visuals().dark_mode {
|
||||||
background_color = color_from_hex("#3f3f3f").unwrap();
|
background_color = color_from_hex("#3f3f3f").unwrap();
|
||||||
titlebar_color = background_color.lighten(0.8);
|
|
||||||
text_color = color_from_hex("#fefefe").unwrap();
|
text_color = color_from_hex("#fefefe").unwrap();
|
||||||
} else {
|
} else {
|
||||||
background_color = color_from_hex("#ffffff").unwrap();
|
background_color = color_from_hex("#ffffff").unwrap();
|
||||||
titlebar_color = background_color.lighten(0.8);
|
|
||||||
text_color = color_from_hex("#505050").unwrap();
|
text_color = color_from_hex("#505050").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,6 +361,7 @@ where
|
|||||||
.text_style(TextStyle::Button)
|
.text_style(TextStyle::Button)
|
||||||
.color(text_color),
|
.color(text_color),
|
||||||
));
|
));
|
||||||
|
ui.add_space(8.0); // The size of the little cross icon
|
||||||
});
|
});
|
||||||
ui.add_space(margin.y);
|
ui.add_space(margin.y);
|
||||||
title_height = ui.min_size().y;
|
title_height = ui.min_size().y;
|
||||||
@ -375,7 +374,13 @@ 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 {
|
||||||
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();
|
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);
|
||||||
@ -406,10 +411,11 @@ where
|
|||||||
let port_right = outer_rect.right();
|
let port_right = outer_rect.right();
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn draw_port<NodeData, DataType, ValueType, UserResponse>(
|
fn draw_port<NodeData, DataType, ValueType, UserResponse, UserState>(
|
||||||
ui: &mut Ui,
|
ui: &mut Ui,
|
||||||
graph: &Graph<NodeData, DataType, ValueType>,
|
graph: &Graph<NodeData, DataType, ValueType>,
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
|
user_state: &UserState,
|
||||||
port_pos: Pos2,
|
port_pos: Pos2,
|
||||||
responses: &mut Vec<NodeResponse<UserResponse>>,
|
responses: &mut Vec<NodeResponse<UserResponse>>,
|
||||||
param_id: AnyParameterId,
|
param_id: AnyParameterId,
|
||||||
@ -417,7 +423,7 @@ where
|
|||||||
ongoing_drag: Option<(NodeId, AnyParameterId)>,
|
ongoing_drag: Option<(NodeId, AnyParameterId)>,
|
||||||
is_connected_input: bool,
|
is_connected_input: bool,
|
||||||
) where
|
) where
|
||||||
DataType: DataTypeTrait,
|
DataType: DataTypeTrait<UserState>,
|
||||||
UserResponse: UserResponseTrait,
|
UserResponse: UserResponseTrait,
|
||||||
{
|
{
|
||||||
let port_type = graph.any_param_type(param_id).unwrap();
|
let port_type = graph.any_param_type(param_id).unwrap();
|
||||||
@ -434,14 +440,21 @@ where
|
|||||||
let port_color = if resp.hovered() {
|
let port_color = if resp.hovered() {
|
||||||
Color32::WHITE
|
Color32::WHITE
|
||||||
} else {
|
} else {
|
||||||
port_type.data_type_color()
|
port_type.data_type_color(user_state)
|
||||||
};
|
};
|
||||||
ui.painter()
|
ui.painter()
|
||||||
.circle(port_rect.center(), 5.0, port_color, Stroke::none());
|
.circle(port_rect.center(), 5.0, port_color, Stroke::none());
|
||||||
|
|
||||||
if resp.drag_started() {
|
if resp.drag_started() {
|
||||||
if is_connected_input {
|
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 {
|
} else {
|
||||||
responses.push(NodeResponse::ConnectEventStarted(node_id, param_id));
|
responses.push(NodeResponse::ConnectEventStarted(node_id, param_id));
|
||||||
}
|
}
|
||||||
@ -454,7 +467,13 @@ where
|
|||||||
&& resp.hovered()
|
&& resp.hovered()
|
||||||
&& ui.input().pointer.any_released()
|
&& 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 */ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -480,6 +499,7 @@ where
|
|||||||
ui,
|
ui,
|
||||||
self.graph,
|
self.graph,
|
||||||
self.node_id,
|
self.node_id,
|
||||||
|
user_state,
|
||||||
pos_left,
|
pos_left,
|
||||||
&mut responses,
|
&mut responses,
|
||||||
AnyParameterId::Input(*param),
|
AnyParameterId::Input(*param),
|
||||||
@ -501,6 +521,7 @@ where
|
|||||||
ui,
|
ui,
|
||||||
self.graph,
|
self.graph,
|
||||||
self.node_id,
|
self.node_id,
|
||||||
|
user_state,
|
||||||
pos_right,
|
pos_right,
|
||||||
&mut responses,
|
&mut responses,
|
||||||
AnyParameterId::Output(*param),
|
AnyParameterId::Output(*param),
|
||||||
@ -511,7 +532,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw the background shape.
|
// 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.
|
// does not support drawing rectangles with asymmetrical round corners.
|
||||||
|
|
||||||
let (shape, outline) = {
|
let (shape, outline) = {
|
||||||
@ -524,7 +545,10 @@ where
|
|||||||
let titlebar = Shape::Rect(RectShape {
|
let titlebar = Shape::Rect(RectShape {
|
||||||
rect: titlebar_rect,
|
rect: titlebar_rect,
|
||||||
rounding,
|
rounding,
|
||||||
fill: titlebar_color,
|
fill: self.graph[self.node_id]
|
||||||
|
.user_data
|
||||||
|
.titlebar_color(ui, self.node_id, self.graph, user_state)
|
||||||
|
.unwrap_or_else(|| background_color.lighten(0.8)),
|
||||||
stroke: Stroke::none(),
|
stroke: Stroke::none(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -63,18 +63,36 @@ impl<NodeData, DataType, ValueType> Graph<NodeData, DataType, ValueType> {
|
|||||||
output_id
|
output_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_node(&mut self, node_id: NodeId) {
|
/// Removes a node from the graph with given `node_id`. This also removes
|
||||||
self.connections
|
/// any incoming or outgoing connections from that node
|
||||||
.retain(|i, o| !(self.outputs[*o].node == node_id || self.inputs[i].node == node_id));
|
///
|
||||||
let inputs: SVec<_> = self[node_id].input_ids().collect();
|
/// This function returns the list of connections that has been removed
|
||||||
for input in inputs {
|
/// 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::<SVec<_>>() {
|
||||||
self.inputs.remove(input);
|
self.inputs.remove(input);
|
||||||
}
|
}
|
||||||
let outputs: SVec<_> = self[node_id].output_ids().collect();
|
for output in self[node_id].output_ids().collect::<SVec<_>>() {
|
||||||
for output in outputs {
|
|
||||||
self.outputs.remove(output);
|
self.outputs.remove(output);
|
||||||
}
|
}
|
||||||
self.nodes.remove(node_id);
|
self.nodes.remove(node_id);
|
||||||
|
|
||||||
|
disconnect_events
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_connection(&mut self, input_id: InputId) -> Option<OutputId> {
|
pub fn remove_connection(&mut self, input_id: InputId) -> Option<OutputId> {
|
||||||
|
|||||||
@ -4,18 +4,50 @@ use super::*;
|
|||||||
/// [`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 {
|
pub trait WidgetValueTrait {
|
||||||
fn value_widget(&mut self, param_name: &str, ui: &mut egui::Ui);
|
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<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
|
||||||
/// [`Graph`]. This trait tells the library how to visually expose data types
|
/// [`Graph`]. This trait tells the library how to visually expose data types
|
||||||
/// to the user.
|
/// to the user.
|
||||||
pub trait DataTypeTrait: PartialEq + Eq {
|
pub trait DataTypeTrait<UserState>: PartialEq + Eq {
|
||||||
// The associated port color of this datatype
|
/// 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
|
/// The name of this datatype. Return type is specified as Cow<str> because
|
||||||
fn name(&self) -> &str;
|
/// 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<str> {
|
||||||
|
/// 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<str> {
|
||||||
|
/// Cow::Owned(format!("Super amazing type #{}", self.some_tag))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn name(&self) -> std::borrow::Cow<str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This trait must be implemented for the `NodeData` generic parameter of the
|
/// This trait must be implemented for the `NodeData` generic parameter of the
|
||||||
@ -43,6 +75,18 @@ where
|
|||||||
) -> Vec<NodeResponse<Self::Response>>
|
) -> Vec<NodeResponse<Self::Response>>
|
||||||
where
|
where
|
||||||
Self::Response: UserResponseTrait;
|
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<Self, Self::DataType, Self::ValueType>,
|
||||||
|
_user_state: &Self::UserState,
|
||||||
|
) -> Option<egui::Color32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This trait can be implemented by any user type. The trait tells the library
|
/// This trait can be implemented by any user type. The trait tells the library
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{borrow::Cow, collections::HashMap};
|
||||||
|
|
||||||
use eframe::egui::{self, DragValue, TextStyle};
|
use eframe::egui::{self, DragValue, TextStyle};
|
||||||
use egui_node_graph::*;
|
use egui_node_graph::*;
|
||||||
@ -89,18 +89,18 @@ pub struct MyGraphState {
|
|||||||
// =========== Then, you need to implement some traits ============
|
// =========== Then, you need to implement some traits ============
|
||||||
|
|
||||||
// A trait for the data types, to tell the library how to display them
|
// A trait for the data types, to tell the library how to display them
|
||||||
impl DataTypeTrait for MyDataType {
|
impl DataTypeTrait<MyGraphState> for MyDataType {
|
||||||
fn data_type_color(&self) -> egui::Color32 {
|
fn data_type_color(&self, _user_state: &MyGraphState) -> egui::Color32 {
|
||||||
match self {
|
match self {
|
||||||
MyDataType::Scalar => egui::Color32::from_rgb(38, 109, 211),
|
MyDataType::Scalar => egui::Color32::from_rgb(38, 109, 211),
|
||||||
MyDataType::Vec2 => egui::Color32::from_rgb(238, 207, 109),
|
MyDataType::Vec2 => egui::Color32::from_rgb(238, 207, 109),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> Cow<'_, str> {
|
||||||
match self {
|
match self {
|
||||||
MyDataType::Scalar => "scalar",
|
MyDataType::Scalar => Cow::Borrowed("scalar"),
|
||||||
MyDataType::Vec2 => "2d vector",
|
MyDataType::Vec2 => Cow::Borrowed("2d vector"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -250,7 +250,8 @@ impl NodeTemplateIter for AllMyNodeTemplates {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WidgetValueTrait for MyValueType {
|
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<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 {
|
||||||
@ -270,6 +271,8 @@ impl WidgetValueTrait for MyValueType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// This allows you to return your responses from the inline widgets.
|
||||||
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +383,7 @@ impl eframe::App for NodeGraphExample {
|
|||||||
Err(err) => format!("Execution error: {}", err),
|
Err(err) => format!("Execution error: {}", err),
|
||||||
};
|
};
|
||||||
ctx.debug_painter().text(
|
ctx.debug_painter().text(
|
||||||
egui::pos2(10.0, 10.0),
|
egui::pos2(10.0, 35.0),
|
||||||
egui::Align2::LEFT_TOP,
|
egui::Align2::LEFT_TOP,
|
||||||
text,
|
text,
|
||||||
TextStyle::Button.resolve(&ctx.style()),
|
TextStyle::Button.resolve(&ctx.style()),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user