1use crate::sys;
2use dear_imgui_rs::sys as imgui_sys;
3use dear_imgui_rs::{Context as ImGuiContext, Ui};
4use std::os::raw::{c_char, c_void};
5
6pub struct Context {
8 raw: *mut sys::ImNodesContext,
9}
10
11impl Context {
12 pub fn try_create(_imgui: &ImGuiContext) -> dear_imgui_rs::ImGuiResult<Self> {
14 unsafe {
15 sys::imnodes_SetImGuiContext(imgui_sys::igGetCurrentContext());
16 }
17 let raw = unsafe { sys::imnodes_CreateContext() };
18 if raw.is_null() {
19 return Err(dear_imgui_rs::ImGuiError::context_creation(
20 "imnodes_CreateContext returned null",
21 ));
22 }
23 unsafe { sys::imnodes_SetCurrentContext(raw) };
24 Ok(Self { raw })
25 }
26
27 pub fn create(imgui: &ImGuiContext) -> Self {
29 Self::try_create(imgui).expect("Failed to create ImNodes context")
30 }
31
32 pub fn set_as_current(&self) {
34 unsafe { sys::imnodes_SetCurrentContext(self.raw) };
35 }
36
37 pub fn as_raw(&self) -> *mut sys::ImNodesContext {
39 self.raw
40 }
41
42 pub fn current_raw() -> Option<std::ptr::NonNull<sys::ImNodesContext>> {
44 let ptr = unsafe { sys::imnodes_GetCurrentContext() };
45 std::ptr::NonNull::new(ptr)
46 }
47}
48
49impl Drop for Context {
50 fn drop(&mut self) {
51 if !self.raw.is_null() {
52 unsafe { sys::imnodes_DestroyContext(self.raw) };
53 }
54 }
55}
56
57pub struct EditorContext {
61 raw: *mut sys::ImNodesEditorContext,
62}
63
64impl EditorContext {
65 pub fn try_create() -> dear_imgui_rs::ImGuiResult<Self> {
66 let raw = unsafe { sys::imnodes_EditorContextCreate() };
67 if raw.is_null() {
68 return Err(dear_imgui_rs::ImGuiError::context_creation(
69 "imnodes_EditorContextCreate returned null",
70 ));
71 }
72 Ok(Self { raw })
73 }
74
75 pub fn create() -> Self {
76 Self::try_create().expect("Failed to create ImNodes editor context")
77 }
78
79 pub fn set_current(&self) {
80 unsafe { sys::imnodes_EditorContextSet(self.raw) };
81 }
82
83 pub fn get_panning(&self) -> [f32; 2] {
84 unsafe { sys::imnodes_EditorContextSet(self.raw) };
85 let out = unsafe { crate::compat_ffi::imnodes_EditorContextGetPanning() };
86 [out.x, out.y]
87 }
88
89 pub fn reset_panning(&self, pos: [f32; 2]) {
90 unsafe { sys::imnodes_EditorContextSet(self.raw) };
91 unsafe {
92 sys::imnodes_EditorContextResetPanning(sys::ImVec2_c {
93 x: pos[0],
94 y: pos[1],
95 })
96 };
97 }
98
99 pub fn move_to_node(&self, node_id: i32) {
100 unsafe { sys::imnodes_EditorContextSet(self.raw) };
101 unsafe { sys::imnodes_EditorContextMoveToNode(node_id) };
102 }
103
104 pub fn save_state_to_ini_string(&self) -> String {
106 unsafe {
107 let mut size: usize = 0;
108 let ptr = sys::imnodes_SaveEditorStateToIniString(self.raw, &mut size as *mut usize);
109 if ptr.is_null() || size == 0 {
110 return String::new();
111 }
112 let mut slice = std::slice::from_raw_parts(ptr as *const u8, size);
113 if slice.last() == Some(&0) {
114 slice = &slice[..slice.len().saturating_sub(1)];
115 }
116 String::from_utf8_lossy(slice).into_owned()
117 }
118 }
119
120 pub fn load_state_from_ini_string(&self, data: &str) {
122 unsafe {
123 sys::imnodes_LoadEditorStateFromIniString(
124 self.raw,
125 data.as_ptr() as *const c_char,
126 data.len(),
127 )
128 }
129 }
130
131 pub fn save_state_to_ini_file(&self, file_name: &str) {
133 let file_name = if file_name.contains('\0') {
134 ""
135 } else {
136 file_name
137 };
138 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
139 sys::imnodes_SaveEditorStateToIniFile(self.raw, ptr)
140 })
141 }
142
143 pub fn load_state_from_ini_file(&self, file_name: &str) {
145 let file_name = if file_name.contains('\0') {
146 ""
147 } else {
148 file_name
149 };
150 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
151 sys::imnodes_LoadEditorStateFromIniFile(self.raw, ptr)
152 })
153 }
154}
155
156impl Drop for EditorContext {
157 fn drop(&mut self) {
158 if !self.raw.is_null() {
159 unsafe { sys::imnodes_EditorContextFree(self.raw) };
160 }
161 }
162}
163
164pub struct NodesUi<'ui> {
168 _ui: &'ui Ui,
169 _ctx: &'ui Context,
170}
171
172impl<'ui> NodesUi<'ui> {
173 pub(crate) fn new(ui: &'ui Ui, ctx: &'ui Context) -> Self {
174 ctx.set_as_current();
176 Self { _ui: ui, _ctx: ctx }
177 }
178
179 pub fn editor(&self, editor: Option<&'ui EditorContext>) -> NodeEditor<'ui> {
181 NodeEditor::begin(self._ui, editor)
182 }
183}
184
185pub struct NodeEditor<'ui> {
187 _ui: &'ui Ui,
188 ended: bool,
189}
190
191impl<'ui> NodeEditor<'ui> {
192 pub(crate) fn begin(ui: &'ui Ui, editor: Option<&EditorContext>) -> Self {
193 if let Some(ed) = editor {
194 unsafe { sys::imnodes_EditorContextSet(ed.raw) }
195 }
196 unsafe { sys::imnodes_BeginNodeEditor() };
197 Self {
198 _ui: ui,
199 ended: false,
200 }
201 }
202
203 pub fn minimap(&self, size_fraction: f32, location: crate::MiniMapLocation) {
205 unsafe {
206 sys::imnodes_MiniMap(
207 size_fraction,
208 location as sys::ImNodesMiniMapLocation,
209 None,
210 std::ptr::null_mut(),
211 )
212 }
213 }
214
215 pub fn minimap_with_callback<F: FnMut(i32)>(
217 &self,
218 size_fraction: f32,
219 location: crate::MiniMapLocation,
220 callback: &mut F,
221 ) {
222 unsafe extern "C" fn trampoline(node_id: i32, user: *mut c_void) {
223 if user.is_null() {
224 return;
225 }
226 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
227 let closure = &mut *(user as *mut &mut dyn FnMut(i32));
228 (closure)(node_id);
229 }));
230 if res.is_err() {
231 eprintln!("dear-imnodes: panic in minimap callback");
232 std::process::abort();
233 }
234 }
235 let mut cb_obj: &mut dyn FnMut(i32) = callback;
236 let user_ptr = &mut cb_obj as *mut _ as *mut c_void;
237 unsafe {
238 sys::imnodes_MiniMap(
239 size_fraction,
240 location as sys::ImNodesMiniMapLocation,
241 Some(trampoline),
242 user_ptr,
243 )
244 }
245 }
246
247 pub fn node(&self, id: i32) -> NodeToken<'_> {
249 unsafe { sys::imnodes_BeginNode(id) };
250 NodeToken {
251 _phantom: std::marker::PhantomData,
252 }
253 }
254
255 pub fn input_attr(&self, id: i32, shape: crate::PinShape) -> AttributeToken<'_> {
257 unsafe { sys::imnodes_BeginInputAttribute(id, shape as sys::ImNodesPinShape) };
258 AttributeToken {
259 kind: AttrKind::Input,
260 _phantom: std::marker::PhantomData,
261 }
262 }
263
264 pub fn output_attr(&self, id: i32, shape: crate::PinShape) -> AttributeToken<'_> {
266 unsafe { sys::imnodes_BeginOutputAttribute(id, shape as i32) };
267 AttributeToken {
268 kind: AttrKind::Output,
269 _phantom: std::marker::PhantomData,
270 }
271 }
272
273 pub fn static_attr(&self, id: i32) -> AttributeToken<'_> {
275 unsafe { sys::imnodes_BeginStaticAttribute(id) };
276 AttributeToken {
277 kind: AttrKind::Static,
278 _phantom: std::marker::PhantomData,
279 }
280 }
281
282 pub fn link(&self, id: i32, start_attr: i32, end_attr: i32) {
284 unsafe { sys::imnodes_Link(id, start_attr, end_attr) }
285 }
286
287 pub fn is_link_created(&self) -> Option<crate::LinkCreated> {
289 let mut start_attr = 0i32;
290 let mut end_attr = 0i32;
291 let mut from_snap = false;
292 let created = unsafe {
293 sys::imnodes_IsLinkCreated_BoolPtr(
294 &mut start_attr as *mut i32,
295 &mut end_attr as *mut i32,
296 &mut from_snap as *mut bool,
297 )
298 };
299 if created {
300 Some(crate::LinkCreated {
301 start_attr,
302 end_attr,
303 from_snap,
304 })
305 } else {
306 None
307 }
308 }
309
310 pub fn is_link_created_with_nodes(&self) -> Option<crate::LinkCreatedEx> {
312 let mut start_node = 0i32;
313 let mut start_attr = 0i32;
314 let mut end_node = 0i32;
315 let mut end_attr = 0i32;
316 let mut from_snap = false;
317 let created = unsafe {
318 sys::imnodes_IsLinkCreated_IntPtr(
319 &mut start_node as *mut i32,
320 &mut start_attr as *mut i32,
321 &mut end_node as *mut i32,
322 &mut end_attr as *mut i32,
323 &mut from_snap as *mut bool,
324 )
325 };
326 if created {
327 Some(crate::LinkCreatedEx {
328 start_node,
329 start_attr,
330 end_node,
331 end_attr,
332 from_snap,
333 })
334 } else {
335 None
336 }
337 }
338
339 pub fn selected_nodes(&self) -> Vec<i32> {
341 let n = unsafe { sys::imnodes_NumSelectedNodes() };
342 if n <= 0 {
343 return Vec::new();
344 }
345 let mut buf = vec![0i32; n as usize];
346 unsafe { sys::imnodes_GetSelectedNodes(buf.as_mut_ptr()) };
347 buf
348 }
349
350 pub fn selected_links(&self) -> Vec<i32> {
351 let n = unsafe { sys::imnodes_NumSelectedLinks() };
352 if n <= 0 {
353 return Vec::new();
354 }
355 let mut buf = vec![0i32; n as usize];
356 unsafe { sys::imnodes_GetSelectedLinks(buf.as_mut_ptr()) };
357 buf
358 }
359
360 pub fn clear_selection(&self) {
361 unsafe {
362 sys::imnodes_ClearNodeSelection_Nil();
363 sys::imnodes_ClearLinkSelection_Nil();
364 }
365 }
366
367 pub fn is_editor_hovered(&self) -> bool {
369 unsafe { sys::imnodes_IsEditorHovered() }
370 }
371 pub fn hovered_node(&self) -> Option<i32> {
372 let mut id = 0;
373 if unsafe { sys::imnodes_IsNodeHovered(&mut id) } {
374 Some(id)
375 } else {
376 None
377 }
378 }
379 pub fn hovered_link(&self) -> Option<i32> {
380 let mut id = 0;
381 if unsafe { sys::imnodes_IsLinkHovered(&mut id) } {
382 Some(id)
383 } else {
384 None
385 }
386 }
387 pub fn hovered_pin(&self) -> Option<i32> {
388 let mut id = 0;
389 if unsafe { sys::imnodes_IsPinHovered(&mut id) } {
390 Some(id)
391 } else {
392 None
393 }
394 }
395
396 pub fn is_attribute_active(&self) -> bool {
398 unsafe { sys::imnodes_IsAttributeActive() }
399 }
400 pub fn any_attribute_active(&self) -> Option<i32> {
401 let mut id = 0;
402 if unsafe { sys::imnodes_IsAnyAttributeActive(&mut id) } {
403 Some(id)
404 } else {
405 None
406 }
407 }
408
409 pub fn is_link_started(&self) -> Option<i32> {
411 let mut id = 0;
412 if unsafe { sys::imnodes_IsLinkStarted(&mut id) } {
413 Some(id)
414 } else {
415 None
416 }
417 }
418 pub fn is_link_dropped(&self, including_detached: bool) -> Option<i32> {
419 let mut id = 0;
420 if unsafe { sys::imnodes_IsLinkDropped(&mut id, including_detached) } {
421 Some(id)
422 } else {
423 None
424 }
425 }
426
427 pub fn set_style_flag(&self, flag: crate::StyleFlags, enabled: bool) {
429 unsafe {
430 let style = &mut *sys::imnodes_GetStyle();
431 let mut f = style.Flags as i32;
432 let bit = flag.bits();
433 if enabled {
434 f |= bit;
435 } else {
436 f &= !bit;
437 }
438 style.Flags = f;
439 }
440 }
441
442 pub fn enable_link_detach_with_ctrl(&self) {
444 unsafe {
445 let io = &mut *sys::imnodes_GetIO();
446 io.LinkDetachWithModifierClick.Modifier = sys::getIOKeyCtrlPtr();
447 }
448 }
449 pub fn enable_multiple_select_with_ctrl(&self) {
451 unsafe {
452 let io = &mut *sys::imnodes_GetIO();
453 io.MultipleSelectModifier.Modifier = sys::getIOKeyCtrlPtr();
454 }
455 }
456 pub fn enable_multiple_select_with_shift(&self) {
458 unsafe {
459 let io = &mut *sys::imnodes_GetIO();
460 io.MultipleSelectModifier.Modifier = sys::imnodes_getIOKeyShiftPtr();
461 }
462 }
463 pub fn emulate_three_button_mouse_with_alt(&self) {
465 unsafe {
466 let io = &mut *sys::imnodes_GetIO();
467 io.EmulateThreeButtonMouse.Modifier = sys::imnodes_getIOKeyAltPtr();
468 }
469 }
470 pub fn set_alt_mouse_button(&self, button: i32) {
472 unsafe {
473 (*sys::imnodes_GetIO()).AltMouseButton = button;
474 }
475 }
476 pub fn set_auto_panning_speed(&self, speed: f32) {
477 unsafe {
478 (*sys::imnodes_GetIO()).AutoPanningSpeed = speed;
479 }
480 }
481 pub fn style_colors_dark(&self) {
483 unsafe { sys::imnodes_StyleColorsDark(sys::imnodes_GetStyle()) }
484 }
485 pub fn style_colors_classic(&self) {
486 unsafe { sys::imnodes_StyleColorsClassic(sys::imnodes_GetStyle()) }
487 }
488 pub fn style_colors_light(&self) {
489 unsafe { sys::imnodes_StyleColorsLight(sys::imnodes_GetStyle()) }
490 }
491
492 pub fn set_node_pos_grid(&self, node_id: i32, pos: [f32; 2]) {
496 unsafe {
497 sys::imnodes_SetNodeGridSpacePos(
498 node_id,
499 sys::ImVec2_c {
500 x: pos[0],
501 y: pos[1],
502 },
503 )
504 }
505 }
506
507 pub fn get_node_pos_grid(&self, node_id: i32) -> [f32; 2] {
508 let out = unsafe { sys::imnodes_GetNodeGridSpacePos(node_id) };
509 [out.x, out.y]
510 }
511
512 pub fn set_grid_spacing(&self, spacing: f32) {
514 unsafe {
515 (*sys::imnodes_GetStyle()).GridSpacing = spacing;
516 }
517 }
518 pub fn set_link_thickness(&self, thickness: f32) {
519 unsafe {
520 (*sys::imnodes_GetStyle()).LinkThickness = thickness;
521 }
522 }
523 pub fn set_node_corner_rounding(&self, rounding: f32) {
524 unsafe {
525 (*sys::imnodes_GetStyle()).NodeCornerRounding = rounding;
526 }
527 }
528 pub fn set_node_padding(&self, padding: [f32; 2]) {
529 unsafe {
530 (*sys::imnodes_GetStyle()).NodePadding = sys::ImVec2_c {
531 x: padding[0],
532 y: padding[1],
533 };
534 }
535 }
536 pub fn set_pin_circle_radius(&self, r: f32) {
537 unsafe {
538 (*sys::imnodes_GetStyle()).PinCircleRadius = r;
539 }
540 }
541 pub fn set_pin_quad_side_length(&self, v: f32) {
542 unsafe {
543 (*sys::imnodes_GetStyle()).PinQuadSideLength = v;
544 }
545 }
546 pub fn set_pin_triangle_side_length(&self, v: f32) {
547 unsafe {
548 (*sys::imnodes_GetStyle()).PinTriangleSideLength = v;
549 }
550 }
551 pub fn set_pin_line_thickness(&self, v: f32) {
552 unsafe {
553 (*sys::imnodes_GetStyle()).PinLineThickness = v;
554 }
555 }
556 pub fn set_pin_hover_radius(&self, v: f32) {
557 unsafe {
558 (*sys::imnodes_GetStyle()).PinHoverRadius = v;
559 }
560 }
561 pub fn set_pin_offset(&self, offset: f32) {
562 unsafe {
563 (*sys::imnodes_GetStyle()).PinOffset = offset;
564 }
565 }
566 pub fn set_link_hover_distance(&self, v: f32) {
567 unsafe {
568 (*sys::imnodes_GetStyle()).LinkHoverDistance = v;
569 }
570 }
571 pub fn set_link_line_segments_per_length(&self, v: f32) {
572 unsafe {
573 (*sys::imnodes_GetStyle()).LinkLineSegmentsPerLength = v;
574 }
575 }
576 pub fn set_node_border_thickness(&self, v: f32) {
577 unsafe {
578 (*sys::imnodes_GetStyle()).NodeBorderThickness = v;
579 }
580 }
581 pub fn set_minimap_padding(&self, padding: [f32; 2]) {
582 unsafe {
583 (*sys::imnodes_GetStyle()).MiniMapPadding = sys::ImVec2_c {
584 x: padding[0],
585 y: padding[1],
586 };
587 }
588 }
589 pub fn set_minimap_offset(&self, offset: [f32; 2]) {
590 unsafe {
591 (*sys::imnodes_GetStyle()).MiniMapOffset = sys::ImVec2_c {
592 x: offset[0],
593 y: offset[1],
594 };
595 }
596 }
597
598 pub fn set_color(&self, elem: crate::style::ColorElement, color: [f32; 4]) {
599 let abgr = unsafe {
600 imgui_sys::igColorConvertFloat4ToU32(imgui_sys::ImVec4 {
601 x: color[0],
602 y: color[1],
603 z: color[2],
604 w: color[3],
605 })
606 };
607 unsafe { (*sys::imnodes_GetStyle()).Colors[elem as u32 as usize] = abgr };
608 }
609
610 pub fn get_color(&self, elem: crate::style::ColorElement) -> [f32; 4] {
612 let col = unsafe { (*sys::imnodes_GetStyle()).Colors[elem as u32 as usize] };
613 let out = unsafe { imgui_sys::igColorConvertU32ToFloat4(col) };
614 [out.x, out.y, out.z, out.w]
615 }
616
617 pub fn set_node_pos_screen(&self, node_id: i32, pos: [f32; 2]) {
619 unsafe {
620 sys::imnodes_SetNodeScreenSpacePos(
621 node_id,
622 sys::ImVec2_c {
623 x: pos[0],
624 y: pos[1],
625 },
626 )
627 }
628 }
629 pub fn set_node_pos_editor(&self, node_id: i32, pos: [f32; 2]) {
630 unsafe {
631 sys::imnodes_SetNodeEditorSpacePos(
632 node_id,
633 sys::ImVec2_c {
634 x: pos[0],
635 y: pos[1],
636 },
637 )
638 }
639 }
640 pub fn get_node_pos_screen(&self, node_id: i32) -> [f32; 2] {
641 let out = unsafe { crate::compat_ffi::imnodes_GetNodeScreenSpacePos(node_id) };
642 [out.x, out.y]
643 }
644 pub fn get_node_pos_editor(&self, node_id: i32) -> [f32; 2] {
645 let out = unsafe { crate::compat_ffi::imnodes_GetNodeEditorSpacePos(node_id) };
646 [out.x, out.y]
647 }
648
649 pub fn set_node_draggable(&self, node_id: i32, draggable: bool) {
651 unsafe { sys::imnodes_SetNodeDraggable(node_id, draggable) }
652 }
653 pub fn snap_node_to_grid(&self, node_id: i32) {
654 unsafe { sys::imnodes_SnapNodeToGrid(node_id) }
655 }
656 pub fn get_node_dimensions(&self, node_id: i32) -> [f32; 2] {
657 let out = unsafe { crate::compat_ffi::imnodes_GetNodeDimensions(node_id) };
658 [out.x, out.y]
659 }
660
661 pub fn is_link_destroyed(&self) -> Option<i32> {
663 let mut id = 0i32;
664 let destroyed = unsafe { sys::imnodes_IsLinkDestroyed(&mut id as *mut i32) };
665 if destroyed { Some(id) } else { None }
666 }
667}
668
669impl<'ui> Drop for NodeEditor<'ui> {
670 fn drop(&mut self) {
671 if !self.ended {
672 unsafe { sys::imnodes_EndNodeEditor() };
673 self.ended = true;
674 }
675 }
676}
677
678pub struct NodeToken<'a> {
680 pub(crate) _phantom: std::marker::PhantomData<&'a ()>,
681}
682impl<'a> NodeToken<'a> {
683 pub fn title_bar<F: FnOnce()>(&self, f: F) {
684 unsafe { sys::imnodes_BeginNodeTitleBar() };
685 f();
686 unsafe { sys::imnodes_EndNodeTitleBar() };
687 }
688 pub fn end(self) {}
689}
690impl<'a> Drop for NodeToken<'a> {
691 fn drop(&mut self) {
692 unsafe { sys::imnodes_EndNode() }
693 }
694}
695
696pub(crate) enum AttrKind {
697 Input,
698 Output,
699 Static,
700}
701
702pub struct AttributeToken<'a> {
703 pub(crate) kind: AttrKind,
704 pub(crate) _phantom: std::marker::PhantomData<&'a ()>,
705}
706
707impl<'a> AttributeToken<'a> {
708 pub fn end(self) {}
709}
710
711impl<'a> Drop for AttributeToken<'a> {
712 fn drop(&mut self) {
713 unsafe {
714 match self.kind {
715 AttrKind::Input => sys::imnodes_EndInputAttribute(),
716 AttrKind::Output => sys::imnodes_EndOutputAttribute(),
717 AttrKind::Static => sys::imnodes_EndStaticAttribute(),
718 }
719 }
720 }
721}
722
723pub struct PostEditor;
725
726impl<'ui> NodeEditor<'ui> {
727 pub fn end(mut self) -> PostEditor {
729 if !self.ended {
730 unsafe { sys::imnodes_EndNodeEditor() };
731 self.ended = true;
732 }
733 PostEditor
734 }
735}
736
737impl PostEditor {
738 pub fn save_state_to_ini_string(&self) -> String {
740 unsafe {
745 let mut size: usize = 0;
746 let ptr = sys::imnodes_SaveCurrentEditorStateToIniString(&mut size as *mut usize);
747 if ptr.is_null() || size == 0 {
748 return String::new();
749 }
750 let mut slice = std::slice::from_raw_parts(ptr as *const u8, size);
751 if slice.last() == Some(&0) {
752 slice = &slice[..slice.len().saturating_sub(1)];
753 }
754 String::from_utf8_lossy(slice).into_owned()
755 }
756 }
757
758 pub fn load_state_from_ini_string(&self, data: &str) {
760 unsafe {
764 sys::imnodes_LoadCurrentEditorStateFromIniString(
765 data.as_ptr() as *const c_char,
766 data.len(),
767 );
768 }
769 }
770 pub fn save_state_to_ini_file(&self, file_name: &str) {
772 let file_name = if file_name.contains('\0') {
773 ""
774 } else {
775 file_name
776 };
777 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
779 sys::imnodes_SaveCurrentEditorStateToIniFile(ptr)
780 })
781 }
782 pub fn load_state_from_ini_file(&self, file_name: &str) {
783 let file_name = if file_name.contains('\0') {
784 ""
785 } else {
786 file_name
787 };
788 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
790 sys::imnodes_LoadCurrentEditorStateFromIniFile(ptr)
791 })
792 }
793 pub fn select_node(&self, node_id: i32) {
795 unsafe { sys::imnodes_SelectNode(node_id) }
796 }
797 pub fn clear_node_selection_of(&self, node_id: i32) {
798 unsafe { sys::imnodes_ClearNodeSelection_Int(node_id) }
799 }
800 pub fn is_node_selected(&self, node_id: i32) -> bool {
801 unsafe { sys::imnodes_IsNodeSelected(node_id) }
802 }
803 pub fn select_link(&self, link_id: i32) {
804 unsafe { sys::imnodes_SelectLink(link_id) }
805 }
806 pub fn clear_link_selection_of(&self, link_id: i32) {
807 unsafe { sys::imnodes_ClearLinkSelection_Int(link_id) }
808 }
809 pub fn is_link_selected(&self, link_id: i32) -> bool {
810 unsafe { sys::imnodes_IsLinkSelected(link_id) }
811 }
812 pub fn selected_nodes(&self) -> Vec<i32> {
813 let n = unsafe { sys::imnodes_NumSelectedNodes() };
816 if n <= 0 {
817 return Vec::new();
818 }
819 let mut buf = vec![0i32; n as usize];
820 unsafe { sys::imnodes_GetSelectedNodes(buf.as_mut_ptr()) };
821 buf
822 }
823
824 pub fn selected_links(&self) -> Vec<i32> {
825 let n = unsafe { sys::imnodes_NumSelectedLinks() };
828 if n <= 0 {
829 return Vec::new();
830 }
831 let mut buf = vec![0i32; n as usize];
832 unsafe { sys::imnodes_GetSelectedLinks(buf.as_mut_ptr()) };
833 buf
834 }
835
836 pub fn clear_selection(&self) {
837 unsafe {
838 sys::imnodes_ClearNodeSelection_Nil();
839 sys::imnodes_ClearLinkSelection_Nil();
840 }
841 }
842
843 pub fn is_link_created(&self) -> Option<crate::LinkCreated> {
844 let mut start_attr = 0i32;
845 let mut end_attr = 0i32;
846 let mut from_snap = false;
847 let created = unsafe {
848 sys::imnodes_IsLinkCreated_BoolPtr(
849 &mut start_attr as *mut i32,
850 &mut end_attr as *mut i32,
851 &mut from_snap as *mut bool,
852 )
853 };
854 if created {
855 Some(crate::LinkCreated {
856 start_attr,
857 end_attr,
858 from_snap,
859 })
860 } else {
861 None
862 }
863 }
864
865 pub fn is_link_created_with_nodes(&self) -> Option<crate::LinkCreatedEx> {
866 let mut start_node = 0i32;
867 let mut start_attr = 0i32;
868 let mut end_node = 0i32;
869 let mut end_attr = 0i32;
870 let mut from_snap = false;
871 let created = unsafe {
872 sys::imnodes_IsLinkCreated_IntPtr(
873 &mut start_node as *mut i32,
874 &mut start_attr as *mut i32,
875 &mut end_node as *mut i32,
876 &mut end_attr as *mut i32,
877 &mut from_snap as *mut bool,
878 )
879 };
880 if created {
881 Some(crate::LinkCreatedEx {
882 start_node,
883 start_attr,
884 end_node,
885 end_attr,
886 from_snap,
887 })
888 } else {
889 None
890 }
891 }
892
893 pub fn is_link_destroyed(&self) -> Option<i32> {
894 let mut id = 0i32;
895 let destroyed = unsafe { sys::imnodes_IsLinkDestroyed(&mut id as *mut i32) };
896 if destroyed { Some(id) } else { None }
897 }
898
899 pub fn is_editor_hovered(&self) -> bool {
900 unsafe { sys::imnodes_IsEditorHovered() }
901 }
902 pub fn hovered_node(&self) -> Option<i32> {
903 let mut id = 0;
904 if unsafe { sys::imnodes_IsNodeHovered(&mut id) } {
905 Some(id)
906 } else {
907 None
908 }
909 }
910 pub fn hovered_link(&self) -> Option<i32> {
911 let mut id = 0;
912 if unsafe { sys::imnodes_IsLinkHovered(&mut id) } {
913 Some(id)
914 } else {
915 None
916 }
917 }
918 pub fn hovered_pin(&self) -> Option<i32> {
919 let mut id = 0;
920 if unsafe { sys::imnodes_IsPinHovered(&mut id) } {
921 Some(id)
922 } else {
923 None
924 }
925 }
926 pub fn is_attribute_active(&self) -> bool {
927 unsafe { sys::imnodes_IsAttributeActive() }
928 }
929 pub fn any_attribute_active(&self) -> Option<i32> {
930 let mut id = 0;
931 if unsafe { sys::imnodes_IsAnyAttributeActive(&mut id) } {
932 Some(id)
933 } else {
934 None
935 }
936 }
937 pub fn is_link_started(&self) -> Option<i32> {
938 let mut id = 0;
939 if unsafe { sys::imnodes_IsLinkStarted(&mut id) } {
940 Some(id)
941 } else {
942 None
943 }
944 }
945 pub fn is_link_dropped(&self, including_detached: bool) -> Option<i32> {
946 let mut id = 0;
947 if unsafe { sys::imnodes_IsLinkDropped(&mut id, including_detached) } {
948 Some(id)
949 } else {
950 None
951 }
952 }
953}