egui_nodes/
lib.rs

1//! egui_nodes: A Egui port of [imnodes](https://github.com/Nelarius/imnodes)
2//!
3//! # Using egui_nodes
4//!
5//! There are some simple examples [here](https://github.com/theoparis/egui_nodes/tree/main/examples)
6//!
7//! Here is the basic usage:
8//! ``` rust
9//! use egui_nodes::{Context, NodeConstructor, LinkArgs};
10//! use egui::Ui;
11//!
12//! pub fn example_graph(ctx: &mut Context, links: &mut Vec<(usize, usize)>, ui: &mut Ui) {
13//!     // add nodes with attributes
14//!     let nodes = vec![
15//!         NodeConstructor::new(0, Default::default())
16//!             .with_title(|ui| ui.label("Example Node A"))
17//!             .with_input_attribute(0, Default::default(), |ui| ui.label("Input"))
18//!             .with_static_attribute(1, |ui| ui.label("Can't Connect to Me"))
19//!             .with_output_attribute(2, Default::default(), |ui| ui.label("Output")),
20//!         NodeConstructor::new(1, Default::default())
21//!             .with_title(|ui| ui.label("Example Node B"))
22//!             .with_static_attribute(3, |ui| ui.label("Can't Connect to Me"))
23//!             .with_output_attribute(4, Default::default(), |ui| ui.label("Output"))
24//!             .with_input_attribute(5, Default::default(), |ui| ui.label("Input"))
25//!     ];
26//!
27//!     // add them to the ui
28//!     ctx.show(
29//!         nodes,
30//!         links.iter().enumerate().map(|(i, (start, end))| (i, *start, *end, LinkArgs::default())),
31//!         ui
32//!     );
33//!     
34//!     // remove destroyed links
35//!     if let Some(idx) = ctx.link_destroyed() {
36//!         links.remove(idx);
37//!     }
38//!
39//!     // add created links
40//!     if let Some((start, end, _)) = ctx.link_created() {
41//!         links.push((start, end))
42//!     }
43//! }
44//! ```
45
46use derivative::Derivative;
47use egui::UiBuilder;
48use std::collections::HashMap;
49
50mod link;
51mod node;
52mod pin;
53mod style;
54
55use link::*;
56use node::*;
57use pin::*;
58
59pub use {
60    link::LinkArgs,
61    node::{NodeArgs, NodeConstructor},
62    pin::{AttributeFlags, PinArgs, PinShape},
63    style::{ColorStyle, Style, StyleFlags, StyleVar},
64};
65
66/// The Context that tracks the state of the node editor
67#[derive(Derivative)]
68#[derivative(Default, Debug)]
69pub struct Context {
70    node_idx_submission_order: Vec<usize>,
71    node_indices_overlapping_with_mouse: Vec<usize>,
72    occluded_pin_indices: Vec<usize>,
73
74    canvas_origin_screen_space: egui::Vec2,
75    #[derivative(Default(value = "[[0.0; 2].into(); 2].into()"))]
76    canvas_rect_screen_space: egui::Rect,
77
78    #[derivative(Debug = "ignore")]
79    pub io: IO,
80    #[derivative(Debug = "ignore")]
81    pub style: Style,
82    color_modifier_stack: Vec<ColorStyleElement>,
83    style_modifier_stack: Vec<StyleElement>,
84    text_buffer: String,
85
86    current_attribute_flags: usize,
87    attribute_flag_stack: Vec<usize>,
88
89    hovered_node_index: Option<usize>,
90    interactive_node_index: Option<usize>,
91    hovered_link_idx: Option<usize>,
92    hovered_pin_index: Option<usize>,
93    hovered_pin_flags: usize,
94    ui_element_hovered: bool,
95
96    deleted_link_idx: Option<usize>,
97    snap_link_idx: Option<usize>,
98
99    element_state_change: usize,
100
101    active_attribute_id: usize,
102    active_attribute: bool,
103
104    mouse_pos: egui::Pos2,
105    mouse_delta: egui::Vec2,
106
107    left_mouse_clicked: bool,
108    left_mouse_released: bool,
109    alt_mouse_clicked: bool,
110    left_mouse_dragging: bool,
111    alt_mouse_dragging: bool,
112    mouse_in_canvas: bool,
113    link_detatch_with_modifier_click: bool,
114
115    nodes: ObjectPool<NodeData>,
116    pins: ObjectPool<PinData>,
117    links: ObjectPool<LinkData>,
118    nodes_map: HashMap<usize, usize>,
119    nodes_free: Vec<usize>,
120
121    node_depth_order: Vec<usize>,
122
123    panning: egui::Vec2,
124
125    selected_node_indices: Vec<usize>,
126    selected_link_indices: Vec<usize>,
127
128    #[derivative(Default(value = "ClickInteractionType::None"))]
129    click_interaction_type: ClickInteractionType,
130    click_interaction_state: ClickInteractionState,
131}
132
133impl Context {
134    /// Displays the current state of the editor on a give Egui Ui as well as updating user input to the context
135    pub fn show<'a>(
136        &mut self,
137        nodes: impl IntoIterator<Item = NodeConstructor<'a>>,
138        links: impl IntoIterator<Item = (usize, usize, usize, LinkArgs)>,
139        ui: &mut egui::Ui,
140    ) -> egui::Response {
141        let rect = ui.available_rect_before_wrap();
142        self.canvas_rect_screen_space = rect;
143        self.canvas_origin_screen_space = self.canvas_rect_screen_space.min.to_vec2();
144        {
145            self.nodes.reset();
146            self.pins.reset();
147            self.links.reset();
148
149            self.hovered_node_index.take();
150            self.interactive_node_index.take();
151            self.hovered_link_idx.take();
152            self.hovered_pin_flags = AttributeFlags::None as usize;
153            self.deleted_link_idx.take();
154            self.snap_link_idx.take();
155
156            self.node_indices_overlapping_with_mouse.clear();
157            self.element_state_change = ElementStateChange::None as usize;
158
159            self.active_attribute = false;
160        }
161
162        {
163            ui.set_min_size(self.canvas_rect_screen_space.size());
164            let mut ui = ui.new_child(
165                UiBuilder::new()
166                    .max_rect(self.canvas_rect_screen_space)
167                    .layout(egui::Layout::top_down(egui::Align::Center)),
168            );
169            {
170                let ui = &mut ui;
171                let screen_rect = ui.ctx().screen_rect();
172                ui.set_clip_rect(self.canvas_rect_screen_space.intersect(screen_rect));
173                ui.painter().rect_filled(
174                    self.canvas_rect_screen_space,
175                    0.0,
176                    self.style.colors[ColorStyle::GridBackground as usize],
177                );
178
179                if (self.style.flags & StyleFlags::GridLines as usize) != 0 {
180                    self.draw_grid(self.canvas_rect_screen_space.size(), ui);
181                }
182
183                let links = links.into_iter().collect::<Vec<_>>();
184                for (id, start, end, args) in links {
185                    self.add_link(id, start, end, args, ui);
186                }
187
188                let mut nodes = nodes
189                    .into_iter()
190                    .map(|x| (self.node_pool_find_or_create_index(x.id, x.pos), x))
191                    .collect::<HashMap<_, _>>();
192                for idx in self.node_depth_order.clone() {
193                    if let Some(node_builder) = nodes.remove(&idx) {
194                        self.add_node(idx, node_builder, ui);
195                    }
196                }
197            }
198            let response = ui.interact(
199                self.canvas_rect_screen_space,
200                ui.id().with("Input"),
201                egui::Sense::click_and_drag(),
202            );
203            let io = ui.ctx().input(|i| i.clone());
204            let mouse_pos = if let Some(mouse_pos) = response.hover_pos() {
205                self.mouse_in_canvas = true;
206                mouse_pos
207            } else {
208                self.mouse_in_canvas = false;
209                self.mouse_pos
210            };
211            self.mouse_delta = mouse_pos - self.mouse_pos;
212            self.mouse_pos = mouse_pos;
213            let left_mouse_clicked = io.pointer.button_down(egui::PointerButton::Primary);
214            self.left_mouse_released =
215                (self.left_mouse_clicked || self.left_mouse_dragging) && !left_mouse_clicked;
216            self.left_mouse_dragging =
217                (self.left_mouse_clicked || self.left_mouse_dragging) && left_mouse_clicked;
218            self.left_mouse_clicked =
219                left_mouse_clicked && !(self.left_mouse_clicked || self.left_mouse_dragging);
220
221            let alt_mouse_clicked = self.io.emulate_three_button_mouse.is_active(&io.modifiers)
222                || self.io.alt_mouse_button.is_some_and(|x| io.pointer.button_down(x));
223            self.alt_mouse_dragging =
224                (self.alt_mouse_clicked || self.alt_mouse_dragging) && alt_mouse_clicked;
225            self.alt_mouse_clicked =
226                alt_mouse_clicked && !(self.alt_mouse_clicked || self.alt_mouse_dragging);
227            self.link_detatch_with_modifier_click =
228                self.io.link_detatch_with_modifier_click.is_active(&io.modifiers);
229            {
230                let ui = &mut ui;
231                if self.mouse_in_canvas {
232                    self.resolve_occluded_pins();
233                    self.resolve_hovered_pin();
234
235                    if self.hovered_pin_index.is_none() {
236                        self.resolve_hovered_node();
237                    }
238
239                    if self.hovered_node_index.is_none() {
240                        self.resolve_hovered_link();
241                    }
242                }
243
244                for node_idx in self.node_depth_order.clone() {
245                    if self.nodes.in_use[node_idx] {
246                        self.draw_node(node_idx, ui);
247                    }
248                }
249
250                for (link_idx, in_use) in self.links.in_use.clone().into_iter().enumerate() {
251                    if in_use {
252                        self.draw_link(link_idx, ui);
253                    }
254                }
255
256                if self.left_mouse_clicked || self.alt_mouse_clicked {
257                    self.begin_canvas_interaction();
258                }
259
260                self.click_interaction_update(ui);
261
262                self.node_pool_update();
263                self.pins.update();
264                self.links.update();
265            }
266            ui.painter().rect_stroke(
267                self.canvas_rect_screen_space,
268                0.0,
269                (1.0, self.style.colors[ColorStyle::GridLine as usize]),
270                egui::StrokeKind::Inside,
271            );
272            response
273        }
274    }
275
276    /// Push a sigle AttributeFlags value, by default only None is set.
277    /// Used for pins that don't have a specific attribute flag specified
278    pub fn attribute_flag_push(&mut self, flag: AttributeFlags) {
279        self.attribute_flag_stack.push(self.current_attribute_flags);
280        self.current_attribute_flags |= flag as usize;
281    }
282
283    /// Remove the last added AttributeFlags value
284    pub fn attribute_flag_pop(&mut self) {
285        if let Some(flags) = self.attribute_flag_stack.pop() {
286            self.current_attribute_flags = flags;
287        }
288    }
289
290    /// Changes the current colors used by the editor
291    pub fn color_style_push(&mut self, item: ColorStyle, color: egui::Color32) {
292        self.color_modifier_stack.push(ColorStyleElement::new(
293            self.style.colors[item as usize],
294            item,
295        ));
296        self.style.colors[item as usize] = color;
297    }
298
299    /// Revert the last color change
300    pub fn color_style_pop(&mut self) {
301        if let Some(elem) = self.color_modifier_stack.pop() {
302            self.style.colors[elem.item as usize] = elem.color;
303        }
304    }
305
306    /// Change a context style value
307    pub fn style_var_push(&mut self, item: StyleVar, value: f32) {
308        let style_var = self.lookup_style_var(item);
309        let elem = StyleElement::new(*style_var, item);
310        *style_var = value;
311        self.style_modifier_stack.push(elem);
312    }
313
314    /// Revert the last context style change
315    pub fn style_var_pop(&mut self) {
316        if let Some(elem) = self.style_modifier_stack.pop() {
317            let style_var = self.lookup_style_var(elem.item);
318            *style_var = elem.value;
319        }
320    }
321
322    pub fn set_node_pos_screen_space(&mut self, node_id: usize, screen_space_pos: egui::Pos2) {
323        let idx = self.node_pool_find_or_create_index(node_id, None);
324        self.nodes.pool[idx].origin = self.screen_space_to_grid_space(screen_space_pos);
325    }
326
327    pub fn set_node_pos_editor_space(&mut self, node_id: usize, editor_space_pos: egui::Pos2) {
328        let idx = self.node_pool_find_or_create_index(node_id, None);
329        self.nodes.pool[idx].origin = self.editor_space_to_grid_spcae(editor_space_pos);
330    }
331
332    pub fn set_node_pos_grid_space(&mut self, node_id: usize, grid_pos: egui::Pos2) {
333        let idx = self.node_pool_find_or_create_index(node_id, None);
334        self.nodes.pool[idx].origin = grid_pos;
335    }
336
337    pub fn set_node_draggable(&mut self, node_id: usize, draggable: bool) {
338        let idx = self.node_pool_find_or_create_index(node_id, None);
339        self.nodes.pool[idx].draggable = draggable;
340    }
341
342    pub fn get_node_pos_screen_space(&self, node_id: usize) -> Option<egui::Pos2> {
343        self.nodes.find(node_id).map(|x| self.grid_space_to_screen_space(self.nodes.pool[x].origin))
344    }
345
346    pub fn get_node_pos_editor_space(&self, node_id: usize) -> Option<egui::Pos2> {
347        self.nodes.find(node_id).map(|x| self.grid_space_to_editor_spcae(self.nodes.pool[x].origin))
348    }
349
350    pub fn get_node_pos_grid_space(&self, node_id: usize) -> Option<egui::Pos2> {
351        self.nodes.find(node_id).map(|x| self.nodes.pool[x].origin)
352    }
353
354    /// Check if there is a node that is hovered by the pointer
355    pub fn node_hovered(&self) -> Option<usize> {
356        self.hovered_node_index.map(|x| self.nodes.pool[x].id)
357    }
358
359    /// Check if there is a link that is hovered by the pointer
360    pub fn link_hovered(&self) -> Option<usize> {
361        self.hovered_link_idx.map(|x| self.links.pool[x].id)
362    }
363
364    /// Check if there is a pin that is hovered by the pointer
365    pub fn pin_hovered(&self) -> Option<usize> {
366        self.hovered_pin_index.map(|x| self.pins.pool[x].id)
367    }
368
369    pub fn num_selected_nodes(&self) -> usize {
370        self.selected_link_indices.len()
371    }
372
373    pub fn get_selected_nodes(&self) -> Vec<usize> {
374        self.selected_node_indices.iter().map(|x| self.nodes.pool[*x].id).collect()
375    }
376
377    pub fn get_selected_links(&self) -> Vec<usize> {
378        self.selected_link_indices.iter().map(|x| self.links.pool[*x].id).collect()
379    }
380
381    pub fn clear_node_selection(&mut self) {
382        self.selected_node_indices.clear()
383    }
384
385    pub fn clear_link_selection(&mut self) {
386        self.selected_link_indices.clear()
387    }
388
389    /// Check if an attribute is currently being interacted with
390    pub fn active_attribute(&self) -> Option<usize> {
391        if self.active_attribute {
392            Some(self.active_attribute_id)
393        } else {
394            None
395        }
396    }
397
398    /// Has a new link been created from a pin?
399    pub fn link_started(&self) -> Option<usize> {
400        if (self.element_state_change & ElementStateChange::LinkStarted as usize) != 0 {
401            Some(self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx].id)
402        } else {
403            None
404        }
405    }
406
407    /// Has a link been dropped? if including_detached_links then links that were detached then dropped are included
408    pub fn link_dropped(&self, including_detached_links: bool) -> Option<usize> {
409        if (self.element_state_change & ElementStateChange::LinkDropped as usize) != 0
410            && (including_detached_links
411                || self.click_interaction_state.link_creation.link_creation_type
412                    != LinkCreationType::FromDetach)
413        {
414            Some(self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx].id)
415        } else {
416            None
417        }
418    }
419
420    /// Has a new link been created?
421    /// -> Option<start_pin, end_pin created_from_snap>
422    pub fn link_created(&self) -> Option<(usize, usize, bool)> {
423        if (self.element_state_change & ElementStateChange::LinkCreated as usize) != 0 {
424            let (start_pin_id, end_pin_id) = {
425                let start_pin =
426                    &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
427                let end_pin = &self.pins.pool
428                    [self.click_interaction_state.link_creation.end_pin_index.unwrap()];
429                if start_pin.kind == AttributeType::Output {
430                    (start_pin.id, end_pin.id)
431                } else {
432                    (end_pin.id, start_pin.id)
433                }
434            };
435            let created_from_snap =
436                self.click_interaction_type == ClickInteractionType::LinkCreation;
437            Some((start_pin_id, end_pin_id, created_from_snap))
438        } else {
439            None
440        }
441    }
442
443    /// Has a new link been created? Includes start and end node
444    /// -> Option<start_pin, start_node, end_pin, end_node created_from_snap>
445    pub fn link_created_node(&self) -> Option<(usize, usize, usize, usize, bool)> {
446        if (self.element_state_change & ElementStateChange::LinkCreated as usize) != 0 {
447            let (start_pin_id, start_node_id, end_pin_id, end_node_id) = {
448                let start_pin =
449                    &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
450                let end_pin = &self.pins.pool
451                    [self.click_interaction_state.link_creation.end_pin_index.unwrap()];
452                let start_node = &self.nodes.pool[start_pin.parent_node_idx];
453                let end_node = &self.nodes.pool[end_pin.parent_node_idx];
454                if start_pin.kind == AttributeType::Output {
455                    (start_pin.id, start_node.id, end_pin.id, end_node.id)
456                } else {
457                    (end_pin.id, end_node.id, start_pin.id, start_node.id)
458                }
459            };
460            let created_from_snap =
461                self.click_interaction_type == ClickInteractionType::LinkCreation;
462            Some((
463                start_pin_id,
464                start_node_id,
465                end_pin_id,
466                end_node_id,
467                created_from_snap,
468            ))
469        } else {
470            None
471        }
472    }
473
474    // Was an existing link detached?
475    pub fn link_destroyed(&self) -> Option<usize> {
476        self.deleted_link_idx
477    }
478
479    pub fn get_panning(&self) -> egui::Vec2 {
480        self.panning
481    }
482
483    pub fn reset_panniing(&mut self, panning: egui::Vec2) {
484        self.panning = panning;
485    }
486
487    pub fn get_node_dimensions(&self, id: usize) -> Option<egui::Vec2> {
488        self.nodes.find(id).map(|x| self.nodes.pool[x].rect.size())
489    }
490}
491
492impl Context {
493    fn add_node(
494        &mut self,
495        idx: usize,
496        NodeConstructor {
497            id,
498            title,
499            attributes,
500            pos: _,
501            args,
502        }: NodeConstructor<'_>,
503        ui: &mut egui::Ui,
504    ) {
505        let node = &mut self.nodes.pool[idx];
506        self.style.format_node(node, args);
507        node.background_shape.replace(ui.painter().add(egui::Shape::Noop));
508        node.id = id;
509        let node_origin = node.origin;
510        let node_size = node.size;
511        let title_space = node.layout_style.padding.y;
512
513        let response = ui.scope_builder(
514            UiBuilder::new().max_rect(egui::Rect::from_min_size(
515                self.grid_space_to_screen_space(node_origin),
516                node_size,
517            )),
518            |ui| {
519                let mut title_info = None;
520                if let Some(title) = title {
521                    let titlebar_shape = ui.painter().add(egui::Shape::Noop);
522                    let response = ui.allocate_ui(ui.available_size(), title);
523                    let title_bar_content_rect = response.response.rect;
524                    title_info.replace((titlebar_shape, title_bar_content_rect));
525                    ui.add_space(title_space);
526                }
527                let outline_shape = ui.painter().add(egui::Shape::Noop);
528                for (id, kind, args, attribute) in attributes {
529                    let response = ui.allocate_ui(ui.available_size(), attribute);
530                    let shape = ui.painter().add(egui::Shape::Noop);
531                    let response = response.response.union(response.inner);
532                    self.add_attribute(id, kind, args, response, idx, shape);
533                }
534                (title_info, outline_shape)
535            },
536        );
537        let node = &mut self.nodes.pool[idx];
538        let (title_info, outline_shape) = response.inner;
539        if let Some((titlebar_shape, title_bar_content_rect)) = title_info {
540            node.titlebar_shape.replace(titlebar_shape);
541            node.title_bar_content_rect = title_bar_content_rect;
542        }
543        node.outline_shape.replace(outline_shape);
544        node.rect = response.response.rect.expand2(node.layout_style.padding);
545        if response.response.hovered() || ui.rect_contains_pointer(node.rect) {
546            self.node_indices_overlapping_with_mouse.push(idx);
547        }
548    }
549
550    fn add_attribute(
551        &mut self,
552        id: usize,
553        kind: AttributeType,
554        args: PinArgs,
555        response: egui::Response,
556        node_idx: usize,
557        shape: egui::layers::ShapeIdx,
558    ) {
559        if kind != AttributeType::None {
560            let pin_idx = self.pins.find_or_create_index(id);
561            let pin = &mut self.pins.pool[pin_idx];
562            pin.id = id;
563            pin.parent_node_idx = node_idx;
564            pin.kind = kind;
565            pin.shape_gui.replace(shape);
566            self.style.format_pin(pin, args, self.current_attribute_flags);
567            self.pins.pool[pin_idx].attribute_rect = response.rect;
568            self.nodes.pool[node_idx].pin_indices.push(pin_idx);
569        }
570
571        if response.is_pointer_button_down_on() {
572            self.active_attribute = true;
573            self.active_attribute_id = id;
574            self.interactive_node_index.replace(node_idx);
575        }
576    }
577
578    fn add_link(
579        &mut self,
580        id: usize,
581        start_attr_id: usize,
582        end_attr_id: usize,
583        args: LinkArgs,
584        ui: &mut egui::Ui,
585    ) {
586        let link_idx = self.links.find_or_create_index(id);
587        let link = &mut self.links.pool[link_idx];
588        link.id = id;
589        link.start_pin_index = self.pins.find_or_create_index(start_attr_id);
590        link.end_pin_index = self.pins.find_or_create_index(end_attr_id);
591        link.shape.replace(ui.painter().add(egui::Shape::Noop));
592        self.style.format_link(link, args);
593
594        if (self.click_interaction_type == ClickInteractionType::LinkCreation
595            && (self.pins.pool[link.end_pin_index].flags
596                & AttributeFlags::EnableLinkCreationOnSnap as usize)
597                != 0
598            && self.click_interaction_state.link_creation.start_pin_idx == link.start_pin_index
599            && self.click_interaction_state.link_creation.end_pin_index == Some(link.end_pin_index))
600            || (self.click_interaction_state.link_creation.start_pin_idx == link.end_pin_index
601                && self.click_interaction_state.link_creation.end_pin_index
602                    == Some(link.start_pin_index))
603        {
604            self.snap_link_idx.replace(link_idx);
605        }
606    }
607
608    fn lookup_style_var(&mut self, item: StyleVar) -> &mut f32 {
609        match item {
610            StyleVar::GridSpacing => &mut self.style.grid_spacing,
611            StyleVar::NodeCornerRounding => &mut self.style.node_corner_rounding,
612            StyleVar::NodePaddingHorizontal => &mut self.style.node_padding_horizontal,
613            StyleVar::NodePaddingVertical => &mut self.style.node_padding_vertical,
614            StyleVar::NodeBorderThickness => &mut self.style.node_border_thickness,
615            StyleVar::LinkThickness => &mut self.style.link_thickness,
616            StyleVar::LinkLineSegmentsPerLength => &mut self.style.link_line_segments_per_length,
617            StyleVar::LinkHoverDistance => &mut self.style.link_hover_distance,
618            StyleVar::PinCircleRadius => &mut self.style.pin_circle_radius,
619            StyleVar::PinQuadSideLength => &mut self.style.pin_quad_side_length,
620            StyleVar::PinTriangleSideLength => &mut self.style.pin_triangle_side_length,
621            StyleVar::PinLineThickness => &mut self.style.pin_line_thickness,
622            StyleVar::PinHoverRadius => &mut self.style.pin_hover_radius,
623            StyleVar::PinOffset => &mut self.style.pin_offset,
624        }
625    }
626
627    fn draw_grid(&self, canvas_size: egui::Vec2, ui: &mut egui::Ui) {
628        let mut x = self.panning.x.rem_euclid(self.style.grid_spacing);
629        while x < canvas_size.x {
630            ui.painter().line_segment(
631                [
632                    self.editor_space_to_screen_space([x, 0.0].into()),
633                    self.editor_space_to_screen_space([x, canvas_size.y].into()),
634                ],
635                (1.0, self.style.colors[ColorStyle::GridLine as usize]),
636            );
637            x += self.style.grid_spacing;
638        }
639
640        let mut y = self.panning.y.rem_euclid(self.style.grid_spacing);
641        while y < canvas_size.y {
642            ui.painter().line_segment(
643                [
644                    self.editor_space_to_screen_space([0.0, y].into()),
645                    self.editor_space_to_screen_space([canvas_size.x, y].into()),
646                ],
647                (1.0, self.style.colors[ColorStyle::GridLine as usize]),
648            );
649            y += self.style.grid_spacing;
650        }
651    }
652
653    fn screen_space_to_grid_space(&self, v: egui::Pos2) -> egui::Pos2 {
654        v - self.canvas_origin_screen_space - self.panning
655    }
656
657    fn grid_space_to_screen_space(&self, v: egui::Pos2) -> egui::Pos2 {
658        v + self.canvas_origin_screen_space + self.panning
659    }
660
661    fn grid_space_to_editor_spcae(&self, v: egui::Pos2) -> egui::Pos2 {
662        v + self.panning
663    }
664
665    fn editor_space_to_grid_spcae(&self, v: egui::Pos2) -> egui::Pos2 {
666        v - self.panning
667    }
668
669    fn editor_space_to_screen_space(&self, v: egui::Pos2) -> egui::Pos2 {
670        v + self.canvas_origin_screen_space
671    }
672
673    fn get_screen_space_pin_coordinates(&self, pin: &PinData) -> egui::Pos2 {
674        let parent_node_rect = self.nodes.pool[pin.parent_node_idx].rect;
675        self.style.get_screen_space_pin_coordinates(
676            &parent_node_rect,
677            &pin.attribute_rect,
678            pin.kind,
679        )
680    }
681
682    fn resolve_occluded_pins(&mut self) {
683        self.occluded_pin_indices.clear();
684        let depth_stack = &self.node_depth_order;
685        if depth_stack.len() < 2 {
686            return;
687        }
688
689        for depth_idx in 0..(depth_stack.len() - 1) {
690            let node_below = &self.nodes.pool[depth_stack[depth_idx]];
691            for next_depth in &depth_stack[(depth_idx + 1)..(depth_stack.len())] {
692                let rect_above = self.nodes.pool[*next_depth].rect;
693                for idx in node_below.pin_indices.iter() {
694                    let pin_pos = self.pins.pool[*idx].pos;
695                    if rect_above.contains(pin_pos) {
696                        self.occluded_pin_indices.push(*idx);
697                    }
698                }
699            }
700        }
701    }
702
703    fn resolve_hovered_pin(&mut self) {
704        let mut smallest_distance = f32::MAX;
705        self.hovered_pin_index.take();
706
707        let hover_radius_sqr = self.style.pin_hover_radius.powi(2);
708        for idx in 0..self.pins.pool.len() {
709            if !self.pins.in_use[idx] || self.occluded_pin_indices.contains(&idx) {
710                continue;
711            }
712
713            let pin_pos = self.pins.pool[idx].pos;
714            let distance_sqr = (pin_pos - self.mouse_pos).length_sq();
715            if distance_sqr < hover_radius_sqr && distance_sqr < smallest_distance {
716                smallest_distance = distance_sqr;
717                self.hovered_pin_index.replace(idx);
718            }
719        }
720    }
721
722    fn resolve_hovered_node(&mut self) {
723        match self.node_indices_overlapping_with_mouse.len() {
724            0 => {
725                self.hovered_node_index.take();
726            }
727            1 => {
728                self.hovered_node_index.replace(self.node_indices_overlapping_with_mouse[0]);
729            }
730            _ => {
731                let mut largest_depth_idx = -1;
732
733                for node_idx in self.node_indices_overlapping_with_mouse.iter() {
734                    for (depth_idx, depth_node_idx) in self.node_depth_order.iter().enumerate() {
735                        if *depth_node_idx == *node_idx && depth_idx as isize > largest_depth_idx {
736                            largest_depth_idx = depth_idx as isize;
737                            self.hovered_node_index.replace(*node_idx);
738                        }
739                    }
740                }
741            }
742        }
743    }
744
745    fn resolve_hovered_link(&mut self) {
746        let mut smallest_distance = f32::MAX;
747        self.hovered_link_idx.take();
748
749        for idx in 0..self.links.pool.len() {
750            if !self.links.in_use[idx] {
751                continue;
752            }
753
754            let link = &self.links.pool[idx];
755            if self.hovered_pin_index == Some(link.start_pin_index)
756                || self.hovered_pin_index == Some(link.end_pin_index)
757            {
758                self.hovered_link_idx.replace(idx);
759                return;
760            }
761
762            let start_pin = &self.pins.pool[link.start_pin_index];
763            let end_pin = &self.pins.pool[link.end_pin_index];
764
765            let link_data = LinkBezierData::get_link_renderable(
766                start_pin.pos,
767                end_pin.pos,
768                start_pin.kind,
769                self.style.link_line_segments_per_length,
770            );
771            let link_rect = link_data
772                .bezier
773                .get_containing_rect_for_bezier_curve(self.style.link_hover_distance);
774
775            if link_rect.contains(self.mouse_pos) {
776                let distance = link_data.get_distance_to_cubic_bezier(&self.mouse_pos);
777                if distance < self.style.link_hover_distance && distance < smallest_distance {
778                    smallest_distance = distance;
779                    self.hovered_link_idx.replace(idx);
780                }
781            }
782        }
783    }
784
785    fn draw_link(&mut self, link_idx: usize, ui: &mut egui::Ui) {
786        let link = &mut self.links.pool[link_idx];
787        let start_pin = &self.pins.pool[link.start_pin_index];
788        let end_pin = &self.pins.pool[link.end_pin_index];
789        let link_data = LinkBezierData::get_link_renderable(
790            start_pin.pos,
791            end_pin.pos,
792            start_pin.kind,
793            self.style.link_line_segments_per_length,
794        );
795        let link_shape = link.shape.take().unwrap();
796        let link_hovered = self.hovered_link_idx == Some(link_idx)
797            && self.click_interaction_type != ClickInteractionType::BoxSelection;
798
799        if link_hovered && self.left_mouse_clicked {
800            self.begin_link_interaction(link_idx);
801        }
802
803        if self.deleted_link_idx == Some(link_idx) {
804            return;
805        }
806
807        let link = &self.links.pool[link_idx];
808        let mut link_color = link.color_style.base;
809        if self.selected_link_indices.contains(&link_idx) {
810            link_color = link.color_style.selected;
811        } else if link_hovered {
812            link_color = link.color_style.hovered;
813        }
814
815        ui.painter().set(
816            link_shape,
817            link_data.draw((self.style.link_thickness, link_color)),
818        );
819    }
820
821    fn draw_node(&mut self, node_idx: usize, ui: &mut egui::Ui) {
822        let node = &mut self.nodes.pool[node_idx];
823
824        let node_hovered = self.hovered_node_index == Some(node_idx)
825            && self.click_interaction_type != ClickInteractionType::BoxSelection;
826
827        let mut node_background = node.color_style.background;
828        let mut titlebar_background = node.color_style.titlebar;
829
830        if self.selected_node_indices.contains(&node_idx) {
831            node_background = node.color_style.background_selected;
832            titlebar_background = node.color_style.titlebar_selected;
833        } else if node_hovered {
834            node_background = node.color_style.background_hovered;
835            titlebar_background = node.color_style.titlebar_hovered;
836        }
837
838        let painter = ui.painter();
839
840        painter.set(
841            node.background_shape.take().unwrap(),
842            egui::Shape::rect_filled(
843                node.rect,
844                node.layout_style.corner_rounding,
845                node_background,
846            ),
847        );
848        if node.title_bar_content_rect.height() > 0.0 {
849            painter.set(
850                node.titlebar_shape.take().unwrap(),
851                egui::Shape::rect_filled(
852                    node.get_node_title_rect(),
853                    node.layout_style.corner_rounding,
854                    titlebar_background,
855                ),
856            );
857        }
858        if (self.style.flags & StyleFlags::NodeOutline as usize) != 0 {
859            painter.set(
860                node.outline_shape.take().unwrap(),
861                egui::Shape::rect_stroke(
862                    node.rect,
863                    node.layout_style.corner_rounding,
864                    (node.layout_style.border_thickness, node.color_style.outline),
865                    egui::StrokeKind::Inside,
866                ),
867            );
868        }
869
870        for pin_idx in node.pin_indices.clone() {
871            self.draw_pin(pin_idx, ui);
872        }
873
874        if node_hovered && self.left_mouse_clicked && self.interactive_node_index != Some(node_idx)
875        {
876            self.begin_node_selection(node_idx);
877        }
878    }
879
880    fn draw_pin(&mut self, pin_idx: usize, ui: &mut egui::Ui) {
881        let pin = &mut self.pins.pool[pin_idx];
882        let parent_node_rect = self.nodes.pool[pin.parent_node_idx].rect;
883
884        pin.pos = self.style.get_screen_space_pin_coordinates(
885            &parent_node_rect,
886            &pin.attribute_rect,
887            pin.kind,
888        );
889
890        let mut pin_color = pin.color_style.background;
891
892        let pin_hovered = self.hovered_pin_index == Some(pin_idx)
893            && self.click_interaction_type != ClickInteractionType::BoxSelection;
894        let pin_shape = pin.shape;
895        let pin_pos = pin.pos;
896        let pin_shape_gui = pin
897            .shape_gui
898            .take()
899            .expect("Unable to take pin shape. Perhaps your pin id is not unique?");
900
901        if pin_hovered {
902            self.hovered_pin_flags = pin.flags;
903            pin_color = pin.color_style.hovered;
904
905            if self.left_mouse_clicked {
906                self.begin_link_creation(pin_idx);
907            }
908        }
909
910        self.style.draw_pin_shape(pin_pos, pin_shape, pin_color, pin_shape_gui, ui);
911    }
912
913    fn begin_canvas_interaction(&mut self) {
914        let any_ui_element_hovered = self.hovered_node_index.is_some()
915            || self.hovered_link_idx.is_some()
916            || self.hovered_pin_index.is_some();
917
918        let mouse_not_in_canvas = !self.mouse_in_canvas;
919
920        if self.click_interaction_type != ClickInteractionType::None
921            || any_ui_element_hovered
922            || mouse_not_in_canvas
923        {
924            return;
925        }
926
927        if self.alt_mouse_clicked {
928            self.click_interaction_type = ClickInteractionType::Panning;
929        } else {
930            self.click_interaction_type = ClickInteractionType::BoxSelection;
931            self.click_interaction_state.box_selection.min = self.mouse_pos;
932        }
933    }
934
935    fn translate_selected_nodes(&mut self) {
936        if self.left_mouse_dragging {
937            let delta = self.mouse_delta;
938            for idx in self.selected_node_indices.iter() {
939                let node = &mut self.nodes.pool[*idx];
940                if node.draggable {
941                    node.origin += delta;
942                }
943            }
944        }
945    }
946
947    fn should_link_snap_to_pin(
948        &self,
949        start_pin: &PinData,
950        hovered_pin_idx: usize,
951        duplicate_link: Option<usize>,
952    ) -> bool {
953        let end_pin = &self.pins.pool[hovered_pin_idx];
954        if start_pin.parent_node_idx == end_pin.parent_node_idx {
955            return false;
956        }
957
958        if start_pin.kind == end_pin.kind {
959            return false;
960        }
961
962        if duplicate_link.is_some_and(|x| Some(x) != self.snap_link_idx) {
963            return false;
964        }
965        true
966    }
967
968    fn box_selector_update_selection(&mut self) -> egui::Rect {
969        let mut box_rect = self.click_interaction_state.box_selection;
970        if box_rect.min.x > box_rect.max.x {
971            std::mem::swap(&mut box_rect.min.x, &mut box_rect.max.x);
972        }
973
974        if box_rect.min.y > box_rect.max.y {
975            std::mem::swap(&mut box_rect.min.y, &mut box_rect.max.y);
976        }
977
978        self.selected_node_indices.clear();
979        for (idx, node) in self.nodes.pool.iter().enumerate() {
980            if self.nodes.in_use[idx] && box_rect.intersects(node.rect) {
981                self.selected_node_indices.push(idx);
982            }
983        }
984
985        self.selected_link_indices.clear();
986        for (idx, link) in self.links.pool.iter().enumerate() {
987            if self.links.in_use[idx] {
988                let pin_start = &self.pins.pool[link.start_pin_index];
989                let pin_end = &self.pins.pool[link.end_pin_index];
990                let node_start_rect = self.nodes.pool[pin_start.parent_node_idx].rect;
991                let node_end_rect = self.nodes.pool[pin_end.parent_node_idx].rect;
992                let start = self.style.get_screen_space_pin_coordinates(
993                    &node_start_rect,
994                    &pin_start.attribute_rect,
995                    pin_start.kind,
996                );
997                let end = self.style.get_screen_space_pin_coordinates(
998                    &node_end_rect,
999                    &pin_end.attribute_rect,
1000                    pin_end.kind,
1001                );
1002
1003                if self.rectangle_overlaps_link(&box_rect, &start, &end, pin_start.kind) {
1004                    self.selected_link_indices.push(idx);
1005                }
1006            }
1007        }
1008        box_rect
1009    }
1010
1011    #[inline]
1012    fn rectangle_overlaps_link(
1013        &self,
1014        rect: &egui::Rect,
1015        start: &egui::Pos2,
1016        end: &egui::Pos2,
1017        start_type: AttributeType,
1018    ) -> bool {
1019        let mut lrect = egui::Rect::from_min_max(*start, *end);
1020        if lrect.min.x > lrect.max.x {
1021            std::mem::swap(&mut lrect.min.x, &mut lrect.max.x);
1022        }
1023
1024        if lrect.min.y > lrect.max.y {
1025            std::mem::swap(&mut lrect.min.y, &mut lrect.max.y);
1026        }
1027
1028        if rect.intersects(lrect) {
1029            if rect.contains(*start) || rect.contains(*end) {
1030                return true;
1031            }
1032
1033            let link_data = LinkBezierData::get_link_renderable(
1034                *start,
1035                *end,
1036                start_type,
1037                self.style.link_line_segments_per_length,
1038            );
1039            return link_data.rectangle_overlaps_bezier(rect);
1040        }
1041        false
1042    }
1043
1044    fn click_interaction_update(&mut self, ui: &mut egui::Ui) {
1045        match self.click_interaction_type {
1046            ClickInteractionType::BoxSelection => {
1047                self.click_interaction_state.box_selection.max = self.mouse_pos;
1048                let rect = self.box_selector_update_selection();
1049
1050                let box_selector_color = self.style.colors[ColorStyle::BoxSelector as usize];
1051                let box_selector_outline =
1052                    self.style.colors[ColorStyle::BoxSelectorOutline as usize];
1053                ui.painter().rect(
1054                    rect,
1055                    0.0,
1056                    box_selector_color,
1057                    (1.0, box_selector_outline),
1058                    egui::StrokeKind::Inside,
1059                );
1060
1061                if self.left_mouse_released {
1062                    let mut idxs = Vec::with_capacity(self.selected_node_indices.len());
1063                    let depth_stack = &mut self.node_depth_order;
1064                    let selected_nodes = &self.selected_node_indices;
1065                    depth_stack.retain(|x| {
1066                        if selected_nodes.contains(x) {
1067                            idxs.push(*x);
1068                            false
1069                        } else {
1070                            true
1071                        }
1072                    });
1073                    self.node_depth_order.extend(idxs);
1074                    self.click_interaction_type = ClickInteractionType::None;
1075                }
1076            }
1077            ClickInteractionType::Node => {
1078                self.translate_selected_nodes();
1079                if self.left_mouse_released {
1080                    self.click_interaction_type = ClickInteractionType::None;
1081                }
1082            }
1083            ClickInteractionType::Link => {
1084                if self.left_mouse_released {
1085                    self.click_interaction_type = ClickInteractionType::None;
1086                }
1087            }
1088            ClickInteractionType::LinkCreation => {
1089                let maybe_duplicate_link_idx = self.hovered_pin_index.and_then(|idx| {
1090                    self.find_duplicate_link(
1091                        self.click_interaction_state.link_creation.start_pin_idx,
1092                        idx,
1093                    )
1094                });
1095
1096                let should_snap = self.hovered_pin_index.is_some_and(|idx| {
1097                    let start_pin =
1098                        &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
1099                    self.should_link_snap_to_pin(start_pin, idx, maybe_duplicate_link_idx)
1100                });
1101
1102                let snapping_pin_changed = self
1103                    .click_interaction_state
1104                    .link_creation
1105                    .end_pin_index
1106                    .is_some_and(|idx| self.hovered_pin_index != Some(idx));
1107
1108                if snapping_pin_changed && self.snap_link_idx.is_some() {
1109                    self.begin_link_detach(
1110                        self.snap_link_idx.unwrap(),
1111                        self.click_interaction_state.link_creation.end_pin_index.unwrap(),
1112                    );
1113                }
1114
1115                let start_pin =
1116                    &self.pins.pool[self.click_interaction_state.link_creation.start_pin_idx];
1117                let start_pos = self.get_screen_space_pin_coordinates(start_pin);
1118
1119                let end_pos = if should_snap {
1120                    self.get_screen_space_pin_coordinates(
1121                        &self.pins.pool[self.hovered_pin_index.unwrap()],
1122                    )
1123                } else {
1124                    self.mouse_pos
1125                };
1126
1127                let link_data = LinkBezierData::get_link_renderable(
1128                    start_pos,
1129                    end_pos,
1130                    start_pin.kind,
1131                    self.style.link_line_segments_per_length,
1132                );
1133                ui.painter().add(link_data.draw((
1134                    self.style.link_thickness,
1135                    self.style.colors[ColorStyle::Link as usize],
1136                )));
1137
1138                let link_creation_on_snap = self.hovered_pin_index.is_some_and(|idx| {
1139                    (self.pins.pool[idx].flags & AttributeFlags::EnableLinkCreationOnSnap as usize)
1140                        != 0
1141                });
1142
1143                if !should_snap {
1144                    self.click_interaction_state.link_creation.end_pin_index.take();
1145                }
1146
1147                let create_link =
1148                    should_snap && (self.left_mouse_released || link_creation_on_snap);
1149
1150                if create_link && maybe_duplicate_link_idx.is_none() {
1151                    if !self.left_mouse_released
1152                        && self.click_interaction_state.link_creation.end_pin_index
1153                            == self.hovered_pin_index
1154                    {
1155                        return;
1156                    }
1157                    self.element_state_change |= ElementStateChange::LinkCreated as usize;
1158                    self.click_interaction_state.link_creation.end_pin_index =
1159                        self.hovered_pin_index;
1160                }
1161
1162                if self.left_mouse_released {
1163                    self.click_interaction_type = ClickInteractionType::None;
1164                    if !create_link {
1165                        self.element_state_change |= ElementStateChange::LinkDropped as usize;
1166                    }
1167                }
1168            }
1169            ClickInteractionType::Panning => {
1170                if self.alt_mouse_dragging || self.alt_mouse_clicked {
1171                    self.panning += self.mouse_delta;
1172                } else {
1173                    self.click_interaction_type = ClickInteractionType::None;
1174                }
1175            }
1176            ClickInteractionType::None => (),
1177        }
1178    }
1179
1180    fn begin_link_detach(&mut self, idx: usize, detach_idx: usize) {
1181        self.click_interaction_state.link_creation.end_pin_index.take();
1182        let link = &self.links.pool[idx];
1183        self.click_interaction_state.link_creation.start_pin_idx =
1184            if detach_idx == link.start_pin_index {
1185                link.end_pin_index
1186            } else {
1187                link.start_pin_index
1188            };
1189        self.deleted_link_idx.replace(idx);
1190    }
1191
1192    fn begin_link_interaction(&mut self, idx: usize) {
1193        if self.click_interaction_type == ClickInteractionType::LinkCreation {
1194            if (self.hovered_pin_flags & AttributeFlags::EnableLinkDetachWithDragClick as usize)
1195                != 0
1196            {
1197                self.begin_link_detach(idx, self.hovered_pin_index.unwrap());
1198                self.click_interaction_state.link_creation.link_creation_type =
1199                    LinkCreationType::FromDetach;
1200            }
1201        } else if self.link_detatch_with_modifier_click {
1202            let link = &self.links.pool[idx];
1203            let start_pin = &self.pins.pool[link.start_pin_index];
1204            let end_pin = &self.pins.pool[link.end_pin_index];
1205            let dist_to_start = start_pin.pos.distance(self.mouse_pos);
1206            let dist_to_end = end_pin.pos.distance(self.mouse_pos);
1207            let closest_pin_idx = if dist_to_start < dist_to_end {
1208                link.start_pin_index
1209            } else {
1210                link.end_pin_index
1211            };
1212            self.click_interaction_type = ClickInteractionType::LinkCreation;
1213            self.begin_link_detach(idx, closest_pin_idx);
1214        } else {
1215            self.begin_link_selection(idx);
1216        }
1217    }
1218
1219    fn begin_link_creation(&mut self, hovered_pin_idx: usize) {
1220        self.click_interaction_type = ClickInteractionType::LinkCreation;
1221        self.click_interaction_state.link_creation.start_pin_idx = hovered_pin_idx;
1222        self.click_interaction_state.link_creation.end_pin_index.take();
1223        self.click_interaction_state.link_creation.link_creation_type = LinkCreationType::Standard;
1224        self.element_state_change |= ElementStateChange::LinkStarted as usize;
1225    }
1226
1227    fn begin_link_selection(&mut self, idx: usize) {
1228        self.click_interaction_type = ClickInteractionType::Link;
1229        self.selected_node_indices.clear();
1230        self.selected_link_indices.clear();
1231        self.selected_link_indices.push(idx);
1232    }
1233
1234    fn find_duplicate_link(&self, start_pin_idx: usize, end_pin_idx: usize) -> Option<usize> {
1235        let mut test_link = LinkData::new(0);
1236        test_link.start_pin_index = start_pin_idx;
1237        test_link.end_pin_index = end_pin_idx;
1238        for (idx, (link, in_use)) in
1239            self.links.pool.iter().zip(self.links.in_use.iter()).enumerate()
1240        {
1241            if *in_use && *link == test_link {
1242                return Some(idx);
1243            }
1244        }
1245        None
1246    }
1247
1248    fn begin_node_selection(&mut self, idx: usize) {
1249        if self.click_interaction_type != ClickInteractionType::None {
1250            return;
1251        }
1252        self.click_interaction_type = ClickInteractionType::Node;
1253        if !self.selected_node_indices.contains(&idx) {
1254            self.selected_node_indices.clear();
1255            self.selected_link_indices.clear();
1256            self.selected_node_indices.push(idx);
1257
1258            self.node_depth_order.retain(|x| *x != idx);
1259            self.node_depth_order.push(idx);
1260        }
1261    }
1262}
1263
1264#[derive(Debug)]
1265enum ElementStateChange {
1266    None = 0,
1267    LinkStarted = 1 << 0,
1268    LinkDropped = 1 << 1,
1269    LinkCreated = 1 << 2,
1270}
1271
1272#[derive(PartialEq, Debug)]
1273enum ClickInteractionType {
1274    Node,
1275    Link,
1276    LinkCreation,
1277    Panning,
1278    BoxSelection,
1279    None,
1280}
1281
1282#[derive(PartialEq, Debug)]
1283enum LinkCreationType {
1284    Standard,
1285    FromDetach,
1286}
1287
1288#[derive(Derivative, Debug)]
1289#[derivative(Default)]
1290struct ClickInteractionStateLinkCreation {
1291    start_pin_idx: usize,
1292    end_pin_index: Option<usize>,
1293    #[derivative(Default(value = "LinkCreationType::Standard"))]
1294    link_creation_type: LinkCreationType,
1295}
1296
1297#[derive(Derivative, Debug)]
1298#[derivative(Default)]
1299struct ClickInteractionState {
1300    link_creation: ClickInteractionStateLinkCreation,
1301    #[derivative(Default(value = "[[0.0; 2].into(); 2].into()"))]
1302    box_selection: egui::Rect,
1303}
1304
1305#[derive(Debug)]
1306struct ColorStyleElement {
1307    color: egui::Color32,
1308    item: ColorStyle,
1309}
1310
1311impl ColorStyleElement {
1312    fn new(color: egui::Color32, item: ColorStyle) -> Self {
1313        Self { color, item }
1314    }
1315}
1316
1317#[derive(Debug)]
1318struct StyleElement {
1319    item: StyleVar,
1320    value: f32,
1321}
1322
1323impl StyleElement {
1324    fn new(value: f32, item: StyleVar) -> Self {
1325        Self { value, item }
1326    }
1327}
1328
1329/// This controls the modifers needed for certain mouse interactions
1330#[derive(Derivative, Debug)]
1331#[derivative(Default)]
1332pub struct IO {
1333    /// The Modfier that needs to pressed to pan the editor
1334    #[derivative(Default(value = "Modifiers::None"))]
1335    pub emulate_three_button_mouse: Modifiers,
1336
1337    // The Modifier that needs to be pressed to detatch a link instead of creating a new one
1338    #[derivative(Default(value = "Modifiers::None"))]
1339    pub link_detatch_with_modifier_click: Modifiers,
1340
1341    // The mouse button that pans the editor. Should probably not be set to Primary.
1342    #[derivative(Default(value = "Some(egui::PointerButton::Middle)"))]
1343    pub alt_mouse_button: Option<egui::PointerButton>,
1344}
1345
1346/// Used to track which Egui Modifier needs to be pressed for certain IO actions
1347#[derive(Debug)]
1348pub enum Modifiers {
1349    Alt,
1350    Crtl,
1351    Shift,
1352    Command,
1353    None,
1354}
1355
1356impl Modifiers {
1357    fn is_active(&self, mods: &egui::Modifiers) -> bool {
1358        match self {
1359            Modifiers::Alt => mods.alt,
1360            Modifiers::Crtl => mods.ctrl,
1361            Modifiers::Shift => mods.shift,
1362            Modifiers::Command => mods.command,
1363            Modifiers::None => false,
1364        }
1365    }
1366}
1367
1368trait Id {
1369    fn id(&self) -> usize;
1370    fn new(id: usize) -> Self;
1371}
1372
1373#[derive(Default, Debug)]
1374struct ObjectPool<T> {
1375    pool: Vec<T>,
1376    in_use: Vec<bool>,
1377    free: Vec<usize>,
1378    map: HashMap<usize, usize>,
1379}
1380
1381impl<T> ObjectPool<T> {
1382    fn find(&self, id: usize) -> Option<usize> {
1383        self.map.get(&id).copied()
1384    }
1385    fn reset(&mut self) {
1386        self.in_use.iter_mut().for_each(|x| *x = false);
1387    }
1388}
1389
1390impl<T: Id> ObjectPool<T> {
1391    fn update(&mut self) {
1392        self.free.clear();
1393        for (i, (in_use, obj)) in self.in_use.iter().zip(self.pool.iter()).enumerate() {
1394            if !*in_use {
1395                self.map.remove(&obj.id());
1396                self.free.push(i);
1397            }
1398        }
1399    }
1400
1401    fn find_or_create_index(&mut self, id: usize) -> usize {
1402        let index = {
1403            if let Some(index) = self.find(id) {
1404                index
1405            } else {
1406                let index = if let Some(index) = self.free.pop() {
1407                    self.pool[index] = T::new(id);
1408                    index
1409                } else {
1410                    self.pool.push(T::new(id));
1411                    self.in_use.push(false);
1412                    self.pool.len() - 1
1413                };
1414                self.map.insert(id, index);
1415                index
1416            }
1417        };
1418        self.in_use[index] = true;
1419        index
1420    }
1421}
1422
1423impl Context {
1424    fn node_pool_update(&mut self) {
1425        self.nodes.free.clear();
1426        for (i, (in_use, node)) in
1427            self.nodes.in_use.iter_mut().zip(self.nodes.pool.iter_mut()).enumerate()
1428        {
1429            if *in_use {
1430                node.pin_indices.clear();
1431            } else {
1432                if self.nodes.map.contains_key(&node.id) {
1433                    self.node_depth_order.retain(|x| *x != i);
1434                }
1435                self.nodes.map.remove(&node.id);
1436                self.nodes.free.push(i);
1437            }
1438        }
1439    }
1440
1441    fn node_pool_find_or_create_index(&mut self, id: usize, origin: Option<egui::Pos2>) -> usize {
1442        let index = {
1443            if let Some(index) = self.nodes.find(id) {
1444                index
1445            } else {
1446                let mut new_node = NodeData::new(id);
1447                if let Some(origin) = origin {
1448                    new_node.origin = self.screen_space_to_grid_space(origin);
1449                }
1450                let index = if let Some(index) = self.nodes.free.pop() {
1451                    self.nodes.pool[index] = new_node;
1452                    index
1453                } else {
1454                    self.nodes.pool.push(new_node);
1455                    self.nodes.in_use.push(false);
1456                    self.nodes.pool.len() - 1
1457                };
1458                self.nodes.map.insert(id, index);
1459                self.node_depth_order.push(index);
1460                index
1461            }
1462        };
1463        self.nodes.in_use[index] = true;
1464        index
1465    }
1466}