173 lines
6.3 KiB
Rust
173 lines
6.3 KiB
Rust
use egui::*;
|
|
|
|
use crate::color_hex_utils::*;
|
|
use crate::device::Device;
|
|
use crate::editor_state::GraphEditorState;
|
|
use crate::graph_node_ui::*;
|
|
use crate::node_finder::{NodeFinder, SelectedDevice};
|
|
use crate::virtual_device::VirtualDevice;
|
|
|
|
pub fn draw_graph_editor(
|
|
ctx: &CtxRef,
|
|
state: &mut GraphEditorState,
|
|
devices: &[Device],
|
|
virtual_device: &mut Option<VirtualDevice>,
|
|
) {
|
|
let mouse = &ctx.input().pointer;
|
|
let cursor_pos = mouse.hover_pos().unwrap_or(Pos2::ZERO);
|
|
|
|
// Gets filled with the port locations as nodes are drawn
|
|
let mut port_locations = PortLocations::new();
|
|
|
|
// The responses returned from node drawing have side effects that are best
|
|
// executed at the end of this function.
|
|
let mut delayed_responses: Vec<DrawGraphNodeResponse> = vec![];
|
|
|
|
CentralPanel::default().show(ctx, |ui| {
|
|
/* Draw nodes */
|
|
let nodes = state.graph.iter_nodes().collect::<Vec<_>>(); // avoid borrow checker
|
|
for node_id in nodes {
|
|
let response = GraphNodeWidget {
|
|
position: state.node_positions.get_mut(&node_id).unwrap(),
|
|
graph: &mut state.graph,
|
|
port_locations: &mut port_locations,
|
|
node_id,
|
|
ongoing_drag: state.connection_in_progress,
|
|
active: state
|
|
.active_node
|
|
.map(|active| active == node_id)
|
|
.unwrap_or(false),
|
|
pan: state.pan_zoom.pan,
|
|
}
|
|
.show(ui);
|
|
|
|
if let Some(response) = response {
|
|
delayed_responses.push(response);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* Draw the node finder, if open */
|
|
let mut should_close_node_finder = false;
|
|
if let Some(ref mut node_finder) = state.node_finder {
|
|
let mut node_finder_area = Area::new("node_finder");
|
|
if let Some(pos) = node_finder.position {
|
|
node_finder_area = node_finder_area.current_pos(pos);
|
|
}
|
|
node_finder_area.show(ctx, |ui| {
|
|
if let Some(node_device) = node_finder.show(devices, virtual_device.is_none(), ui) {
|
|
let new_node = state.graph.add_node(node_device.to_descriptor());
|
|
state
|
|
.node_positions
|
|
.insert(new_node, cursor_pos - state.pan_zoom.pan);
|
|
should_close_node_finder = true;
|
|
|
|
if let SelectedDevice::Virtual(mut virt) = node_device {
|
|
virt.connect_id(new_node);
|
|
*virtual_device = Some(virt);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if should_close_node_finder {
|
|
state.node_finder = None;
|
|
}
|
|
|
|
/* Draw connections */
|
|
let connection_stroke = egui::Stroke {
|
|
width: 5.0,
|
|
color: color_from_hex("#efefef").unwrap(),
|
|
};
|
|
|
|
if let Some((_, ref locator)) = state.connection_in_progress {
|
|
let painter = ctx.layer_painter(LayerId::background());
|
|
let start_pos = port_locations[locator];
|
|
painter.line_segment([start_pos, cursor_pos], connection_stroke)
|
|
}
|
|
|
|
for (input, output) in state.graph.iter_connections() {
|
|
let painter = ctx.layer_painter(LayerId::background());
|
|
let src_pos = port_locations[&output];
|
|
let dst_pos = port_locations[&input];
|
|
painter.line_segment([src_pos, dst_pos], connection_stroke);
|
|
}
|
|
|
|
/* Handle responses from drawing nodes */
|
|
|
|
for response in delayed_responses {
|
|
match response {
|
|
DrawGraphNodeResponse::ConnectEventStarted(node_id, port) => {
|
|
state.connection_in_progress = Some((node_id, port));
|
|
}
|
|
DrawGraphNodeResponse::ConnectEventEnded(locator) => {
|
|
let in_out = match (
|
|
state
|
|
.connection_in_progress
|
|
.map(|(_node, param)| param)
|
|
.take()
|
|
.expect("Cannot end drag without in-progress connection."),
|
|
locator,
|
|
) {
|
|
(input, output) | (output, input) => Some((input, output)),
|
|
_ => None,
|
|
};
|
|
|
|
if let Some((input, output)) = in_out {
|
|
state.graph.add_connection(output, input)
|
|
}
|
|
}
|
|
DrawGraphNodeResponse::SetActiveNode(node_id) => {
|
|
state.active_node = Some(node_id);
|
|
}
|
|
DrawGraphNodeResponse::ClearActiveNode => {
|
|
state.active_node = None;
|
|
}
|
|
DrawGraphNodeResponse::RunNodeSideEffect(node_id) => {
|
|
state.run_side_effect = Some(node_id);
|
|
}
|
|
DrawGraphNodeResponse::DeleteNode(node_id) => {
|
|
if let Some(virt) = virtual_device {
|
|
if node_id == virt.node_id() {
|
|
*virtual_device = None;
|
|
}
|
|
}
|
|
|
|
state.graph.remove_node(node_id);
|
|
state.node_positions.remove(&node_id);
|
|
// Make sure to not leave references to old nodes hanging
|
|
if state.active_node.map(|x| x == node_id).unwrap_or(false) {
|
|
state.active_node = None;
|
|
}
|
|
if state.run_side_effect.map(|x| x == node_id).unwrap_or(false) {
|
|
state.run_side_effect = None;
|
|
}
|
|
}
|
|
DrawGraphNodeResponse::DisconnectEvent(input_id) => {
|
|
let corresp_output = state
|
|
.graph
|
|
.connection(input_id)
|
|
.expect("Connection data should be valid");
|
|
let other_node = state.graph.input(input_id).node;
|
|
state.graph.remove_connection(input_id);
|
|
state.connection_in_progress = Some((other_node, corresp_output));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Mouse input handling */
|
|
|
|
if mouse.any_released() && state.connection_in_progress.is_some() {
|
|
state.connection_in_progress = None;
|
|
}
|
|
|
|
if mouse.button_down(PointerButton::Secondary) {
|
|
state.node_finder = Some(NodeFinder::new_at(cursor_pos));
|
|
}
|
|
if ctx.input().key_pressed(Key::Escape) {
|
|
state.node_finder = None;
|
|
}
|
|
|
|
if ctx.input().pointer.middle_down() {
|
|
state.pan_zoom.pan += ctx.input().pointer.delta();
|
|
}
|
|
}
|