1use crate::{
2 EditorContext, FlowDirection, LinkId, NodeEditorStyle, NodeId, PinId, PinKind, StyleColor,
3 StyleVar, StyleVarType, context::CurrentEditorGuard, from_vec2, sys, vec2, vec4,
4};
5use dear_imgui_rs::{DrawListMut, MouseButton, Ui};
6use std::{cell::Cell, ffi::CString, marker::PhantomData};
7
8pub struct NodeEditorFrame<'ui> {
10 _ui: &'ui Ui,
11 _editor: &'ui EditorContext,
12 _current_editor: CurrentEditorGuard<'ui>,
13 suspended: Cell<bool>,
14 ended: bool,
15}
16
17impl<'ui> NodeEditorFrame<'ui> {
18 pub(crate) fn new(
19 ui: &'ui Ui,
20 editor: &'ui EditorContext,
21 id: impl AsRef<str>,
22 size: [f32; 2],
23 ) -> Self {
24 assert_finite_vec2("Ui::node_editor()", "size", size);
25 let current_editor = editor.bind_current("Ui::node_editor");
26 let id = CString::new(id.as_ref()).expect("node editor id cannot contain NUL bytes");
27 unsafe { sys::dne_begin(id.as_ptr(), vec2(size)) };
28 Self {
29 _ui: ui,
30 _editor: editor,
31 _current_editor: current_editor,
32 suspended: Cell::new(false),
33 ended: false,
34 }
35 }
36
37 pub fn end(mut self) {
38 self.end_inner();
39 }
40
41 fn end_inner(&mut self) {
42 if !self.ended {
43 unsafe { sys::dne_end() };
44 self.ended = true;
45 }
46 }
47
48 pub fn begin_node<'a>(&'a self, node: NodeId) -> NodeToken<'a> {
49 unsafe { sys::dne_begin_node(node.raw()) };
50 NodeToken {
51 ended: false,
52 _scope: PhantomData,
53 }
54 }
55
56 pub fn node<R>(&self, node: NodeId, f: impl FnOnce(&NodeToken<'_>) -> R) -> R {
57 let token = self.begin_node(node);
58 let result = f(&token);
59 token.end();
60 result
61 }
62
63 pub fn begin_group_hint<'a>(&'a self, node: NodeId) -> Option<GroupHintToken<'a>> {
64 unsafe { sys::dne_begin_group_hint(node.raw()) }.then_some(GroupHintToken {
65 ui: self._ui,
66 ended: false,
67 _scope: PhantomData,
68 })
69 }
70
71 pub fn node_background_draw_list(&self, node: NodeId) -> DrawListMut<'_> {
72 let draw_list = unsafe { sys::dne_get_node_background_draw_list(node.raw()) };
73 unsafe { DrawListMut::from_raw_mut(self._ui, draw_list.cast()) }
74 }
75
76 #[doc(alias = "GetStyle")]
77 pub fn style(&self) -> NodeEditorStyle {
78 self._editor.style()
79 }
80
81 pub fn group(&self, size: [f32; 2]) {
82 assert_non_negative_finite_vec2("NodeEditorFrame::group()", "size", size);
83 unsafe { sys::dne_group(vec2(size)) };
84 }
85
86 pub fn set_group_size(&self, node: NodeId, size: [f32; 2]) {
87 assert_non_negative_finite_vec2("NodeEditorFrame::set_group_size()", "size", size);
88 unsafe { sys::dne_set_group_size(node.raw(), vec2(size)) };
89 }
90
91 pub fn link(&self, link: LinkId, start_pin: PinId, end_pin: PinId) -> bool {
92 self.link_colored(link, start_pin, end_pin, [1.0, 1.0, 1.0, 1.0], 1.0)
93 }
94
95 pub fn link_colored(
96 &self,
97 link: LinkId,
98 start_pin: PinId,
99 end_pin: PinId,
100 color: [f32; 4],
101 thickness: f32,
102 ) -> bool {
103 assert_finite_vec4("NodeEditorFrame::link_colored()", "color", color);
104 assert_non_negative_finite_f32("NodeEditorFrame::link_colored()", "thickness", thickness);
105 unsafe {
106 sys::dne_link(
107 link.raw(),
108 start_pin.raw(),
109 end_pin.raw(),
110 vec4(color),
111 thickness,
112 )
113 }
114 }
115
116 pub fn flow(&self, link: LinkId, direction: FlowDirection) {
117 unsafe { sys::dne_flow(link.raw(), direction.raw()) };
118 }
119
120 pub fn begin_create<'a>(
121 &'a self,
122 color: [f32; 4],
123 thickness: f32,
124 ) -> Option<CreateSession<'a>> {
125 assert_finite_vec4("NodeEditorFrame::begin_create()", "color", color);
126 assert_non_negative_finite_f32("NodeEditorFrame::begin_create()", "thickness", thickness);
127 unsafe { sys::dne_begin_create(vec4(color), thickness) }.then_some(CreateSession {
128 ended: false,
129 _scope: PhantomData,
130 })
131 }
132
133 pub fn begin_delete<'a>(&'a self) -> Option<DeleteSession<'a>> {
134 unsafe { sys::dne_begin_delete() }.then_some(DeleteSession {
135 ended: false,
136 _scope: PhantomData,
137 })
138 }
139
140 pub fn begin_shortcut<'a>(&'a self) -> Option<ShortcutSession<'a>> {
141 unsafe { sys::dne_begin_shortcut() }.then_some(ShortcutSession {
142 ended: false,
143 _scope: PhantomData,
144 })
145 }
146
147 pub fn push_style_color<'a>(
148 &'a self,
149 color: StyleColor,
150 value: [f32; 4],
151 ) -> StyleColorToken<'a> {
152 assert_finite_vec4("NodeEditorFrame::push_style_color()", "value", value);
153 unsafe { sys::dne_push_style_color(color.raw(), vec4(value)) };
154 StyleColorToken {
155 count: 1,
156 popped: false,
157 _scope: PhantomData,
158 }
159 }
160
161 pub fn push_style_var_float<'a>(&'a self, var: StyleVar, value: f32) -> StyleVarToken<'a> {
162 assert_style_var_type(
163 "NodeEditorFrame::push_style_var_float()",
164 var,
165 StyleVarType::Float,
166 );
167 assert_finite_f32("NodeEditorFrame::push_style_var_float()", "value", value);
168 unsafe { sys::dne_push_style_var_float(var.raw(), value) };
169 StyleVarToken {
170 count: 1,
171 popped: false,
172 _scope: PhantomData,
173 }
174 }
175
176 pub fn push_style_var_vec2<'a>(&'a self, var: StyleVar, value: [f32; 2]) -> StyleVarToken<'a> {
177 assert_style_var_type(
178 "NodeEditorFrame::push_style_var_vec2()",
179 var,
180 StyleVarType::Vec2,
181 );
182 assert_finite_vec2("NodeEditorFrame::push_style_var_vec2()", "value", value);
183 unsafe { sys::dne_push_style_var_vec2(var.raw(), vec2(value)) };
184 StyleVarToken {
185 count: 1,
186 popped: false,
187 _scope: PhantomData,
188 }
189 }
190
191 pub fn push_style_var_vec4<'a>(&'a self, var: StyleVar, value: [f32; 4]) -> StyleVarToken<'a> {
192 assert_style_var_type(
193 "NodeEditorFrame::push_style_var_vec4()",
194 var,
195 StyleVarType::Vec4,
196 );
197 assert_finite_vec4("NodeEditorFrame::push_style_var_vec4()", "value", value);
198 unsafe { sys::dne_push_style_var_vec4(var.raw(), vec4(value)) };
199 StyleVarToken {
200 count: 1,
201 popped: false,
202 _scope: PhantomData,
203 }
204 }
205
206 pub fn set_node_position(&self, node: NodeId, position: [f32; 2]) {
207 assert_finite_vec2("NodeEditorFrame::set_node_position()", "position", position);
208 unsafe { sys::dne_set_node_position(node.raw(), vec2(position)) };
209 }
210
211 pub fn node_position(&self, node: NodeId) -> [f32; 2] {
212 from_vec2(unsafe { sys::dne_get_node_position(node.raw()) })
213 }
214
215 pub fn node_size(&self, node: NodeId) -> [f32; 2] {
216 from_vec2(unsafe { sys::dne_get_node_size(node.raw()) })
217 }
218
219 pub fn set_node_z_position(&self, node: NodeId, z: f32) {
220 assert_finite_f32("NodeEditorFrame::set_node_z_position()", "z", z);
221 unsafe { sys::dne_set_node_z_position(node.raw(), z) };
222 }
223
224 pub fn node_z_position(&self, node: NodeId) -> f32 {
225 unsafe { sys::dne_get_node_z_position(node.raw()) }
226 }
227
228 pub fn restore_node_state(&self, node: NodeId) {
229 unsafe { sys::dne_restore_node_state(node.raw()) };
230 }
231
232 pub fn center_node_on_screen(&self, node: NodeId) {
233 unsafe { sys::dne_center_node_on_screen(node.raw()) };
234 }
235
236 pub fn navigate_to_content(&self, duration: f32) {
237 assert_finite_f32(
238 "NodeEditorFrame::navigate_to_content()",
239 "duration",
240 duration,
241 );
242 unsafe { sys::dne_navigate_to_content(duration) };
243 }
244
245 pub fn navigate_to_selection(&self, zoom_in: bool, duration: f32) {
246 assert_finite_f32(
247 "NodeEditorFrame::navigate_to_selection()",
248 "duration",
249 duration,
250 );
251 unsafe { sys::dne_navigate_to_selection(zoom_in, duration) };
252 }
253
254 pub fn suspend<'a>(&'a self) -> SuspensionToken<'a> {
255 assert!(
256 !self.suspended.replace(true),
257 "NodeEditorFrame::suspend() cannot be called while the editor is already suspended"
258 );
259 unsafe { sys::dne_suspend() };
260 SuspensionToken {
261 suspended: &self.suspended,
262 resumed: false,
263 }
264 }
265
266 pub fn is_suspended(&self) -> bool {
267 self.suspended.get() || unsafe { sys::dne_is_suspended() }
268 }
269
270 pub fn is_active(&self) -> bool {
271 unsafe { sys::dne_is_active() }
272 }
273
274 pub fn has_selection_changed(&self) -> bool {
275 unsafe { sys::dne_has_selection_changed() }
276 }
277
278 pub fn selected_object_count(&self) -> usize {
279 unsafe { sys::dne_get_selected_object_count() }.max(0) as usize
280 }
281
282 pub fn selected_nodes(&self) -> Vec<NodeId> {
283 let count = self.selected_object_count();
284 collect_node_ids(count, |ptr, len| unsafe {
285 sys::dne_get_selected_nodes(ptr, len)
286 })
287 }
288
289 pub fn selected_links(&self) -> Vec<LinkId> {
290 let count = self.selected_object_count();
291 collect_link_ids(count, |ptr, len| unsafe {
292 sys::dne_get_selected_links(ptr, len)
293 })
294 }
295
296 pub fn is_node_selected(&self, node: NodeId) -> bool {
297 unsafe { sys::dne_is_node_selected(node.raw()) }
298 }
299
300 pub fn is_link_selected(&self, link: LinkId) -> bool {
301 unsafe { sys::dne_is_link_selected(link.raw()) }
302 }
303
304 pub fn clear_selection(&self) {
305 unsafe { sys::dne_clear_selection() };
306 }
307
308 pub fn select_node(&self, node: NodeId) {
309 unsafe { sys::dne_select_node(node.raw(), false) };
310 }
311
312 pub fn add_node_to_selection(&self, node: NodeId) {
313 unsafe { sys::dne_select_node(node.raw(), true) };
314 }
315
316 pub fn select_link(&self, link: LinkId) {
317 unsafe { sys::dne_select_link(link.raw(), false) };
318 }
319
320 pub fn add_link_to_selection(&self, link: LinkId) {
321 unsafe { sys::dne_select_link(link.raw(), true) };
322 }
323
324 pub fn deselect_node(&self, node: NodeId) {
325 unsafe { sys::dne_deselect_node(node.raw()) };
326 }
327
328 pub fn deselect_link(&self, link: LinkId) {
329 unsafe { sys::dne_deselect_link(link.raw()) };
330 }
331
332 pub fn delete_node(&self, node: NodeId) -> bool {
333 unsafe { sys::dne_delete_node(node.raw()) }
334 }
335
336 pub fn delete_link(&self, link: LinkId) -> bool {
337 unsafe { sys::dne_delete_link(link.raw()) }
338 }
339
340 pub fn node_has_any_links(&self, node: NodeId) -> bool {
341 unsafe { sys::dne_has_any_links_node(node.raw()) }
342 }
343
344 pub fn pin_has_any_links(&self, pin: PinId) -> bool {
345 unsafe { sys::dne_has_any_links_pin(pin.raw()) }
346 }
347
348 pub fn break_node_links(&self, node: NodeId) -> usize {
349 unsafe { sys::dne_break_links_node(node.raw()) }.max(0) as usize
350 }
351
352 pub fn break_pin_links(&self, pin: PinId) -> usize {
353 unsafe { sys::dne_break_links_pin(pin.raw()) }.max(0) as usize
354 }
355
356 pub fn hovered_node(&self) -> Option<NodeId> {
357 optional_id(NodeId, |ptr| unsafe { sys::dne_get_hovered_node(ptr) })
358 }
359
360 pub fn hovered_pin(&self) -> Option<PinId> {
361 optional_id(PinId, |ptr| unsafe { sys::dne_get_hovered_pin(ptr) })
362 }
363
364 pub fn hovered_link(&self) -> Option<LinkId> {
365 optional_id(LinkId, |ptr| unsafe { sys::dne_get_hovered_link(ptr) })
366 }
367
368 pub fn double_clicked_node(&self) -> Option<NodeId> {
369 optional_id(NodeId, |ptr| unsafe {
370 sys::dne_get_double_clicked_node(ptr)
371 })
372 }
373
374 pub fn double_clicked_pin(&self) -> Option<PinId> {
375 optional_id(PinId, |ptr| unsafe { sys::dne_get_double_clicked_pin(ptr) })
376 }
377
378 pub fn double_clicked_link(&self) -> Option<LinkId> {
379 optional_id(LinkId, |ptr| unsafe {
380 sys::dne_get_double_clicked_link(ptr)
381 })
382 }
383
384 pub fn show_node_context_menu(&self) -> Option<NodeId> {
385 optional_id(NodeId, |ptr| unsafe {
386 sys::dne_show_node_context_menu(ptr)
387 })
388 }
389
390 pub fn show_pin_context_menu(&self) -> Option<PinId> {
391 optional_id(PinId, |ptr| unsafe { sys::dne_show_pin_context_menu(ptr) })
392 }
393
394 pub fn show_link_context_menu(&self) -> Option<LinkId> {
395 optional_id(LinkId, |ptr| unsafe {
396 sys::dne_show_link_context_menu(ptr)
397 })
398 }
399
400 pub fn show_background_context_menu(&self) -> bool {
401 unsafe { sys::dne_show_background_context_menu() }
402 }
403
404 pub fn set_shortcuts_enabled(&self, enabled: bool) {
405 unsafe { sys::dne_enable_shortcuts(enabled) };
406 }
407
408 pub fn shortcuts_enabled(&self) -> bool {
409 unsafe { sys::dne_are_shortcuts_enabled() }
410 }
411
412 pub fn current_zoom(&self) -> f32 {
413 unsafe { sys::dne_get_current_zoom() }
414 }
415
416 pub fn is_background_clicked(&self) -> bool {
417 unsafe { sys::dne_is_background_clicked() }
418 }
419
420 pub fn is_background_double_clicked(&self) -> bool {
421 unsafe { sys::dne_is_background_double_clicked() }
422 }
423
424 pub fn background_click_button(&self) -> Option<MouseButton> {
425 mouse_button_from_index(unsafe { sys::dne_get_background_click_button_index() })
426 }
427
428 pub fn background_double_click_button(&self) -> Option<MouseButton> {
429 mouse_button_from_index(unsafe { sys::dne_get_background_double_click_button_index() })
430 }
431
432 pub fn link_pins(&self, link: LinkId) -> Option<(PinId, PinId)> {
433 let mut start = 0usize;
434 let mut end = 0usize;
435 unsafe { sys::dne_get_link_pins(link.raw(), &mut start, &mut end) }
436 .then_some((PinId(start), PinId(end)))
437 }
438
439 pub fn pin_had_any_links(&self, pin: PinId) -> bool {
440 unsafe { sys::dne_pin_had_any_links(pin.raw()) }
441 }
442
443 pub fn screen_size(&self) -> [f32; 2] {
444 from_vec2(unsafe { sys::dne_get_screen_size() })
445 }
446
447 pub fn screen_to_canvas(&self, pos: [f32; 2]) -> [f32; 2] {
448 assert_finite_vec2("NodeEditorFrame::screen_to_canvas()", "pos", pos);
449 from_vec2(unsafe { sys::dne_screen_to_canvas(vec2(pos)) })
450 }
451
452 pub fn canvas_to_screen(&self, pos: [f32; 2]) -> [f32; 2] {
453 assert_finite_vec2("NodeEditorFrame::canvas_to_screen()", "pos", pos);
454 from_vec2(unsafe { sys::dne_canvas_to_screen(vec2(pos)) })
455 }
456
457 pub fn node_count(&self) -> usize {
458 unsafe { sys::dne_get_node_count() }.max(0) as usize
459 }
460
461 pub fn ordered_node_ids(&self) -> Vec<NodeId> {
462 let count = self.node_count();
463 collect_node_ids(count, |ptr, len| unsafe {
464 sys::dne_get_ordered_node_ids(ptr, len)
465 })
466 }
467}
468
469impl Drop for NodeEditorFrame<'_> {
470 fn drop(&mut self) {
471 self.end_inner();
472 }
473}
474
475pub struct NodeToken<'a> {
476 ended: bool,
477 _scope: PhantomData<&'a ()>,
478}
479
480impl NodeToken<'_> {
481 pub fn begin_pin<'a>(&'a self, pin: PinId, kind: PinKind) -> PinToken<'a> {
482 unsafe { sys::dne_begin_pin(pin.raw(), kind.raw()) };
483 PinToken {
484 ended: false,
485 _scope: PhantomData,
486 }
487 }
488
489 pub fn pin<R>(&self, pin: PinId, kind: PinKind, f: impl FnOnce(&PinToken<'_>) -> R) -> R {
490 let token = self.begin_pin(pin, kind);
491 let result = f(&token);
492 token.end();
493 result
494 }
495
496 pub fn end(mut self) {
497 self.end_inner();
498 }
499
500 fn end_inner(&mut self) {
501 if !self.ended {
502 unsafe { sys::dne_end_node() };
503 self.ended = true;
504 }
505 }
506}
507
508impl Drop for NodeToken<'_> {
509 fn drop(&mut self) {
510 self.end_inner();
511 }
512}
513
514pub struct PinToken<'a> {
515 ended: bool,
516 _scope: PhantomData<&'a ()>,
517}
518
519impl PinToken<'_> {
520 pub fn end(mut self) {
521 self.end_inner();
522 }
523
524 pub fn rect(&self, min: [f32; 2], max: [f32; 2]) {
525 assert_finite_rect("PinToken::rect()", min, max);
526 unsafe { sys::dne_pin_rect(vec2(min), vec2(max)) };
527 }
528
529 pub fn pivot_rect(&self, min: [f32; 2], max: [f32; 2]) {
530 assert_finite_rect("PinToken::pivot_rect()", min, max);
531 unsafe { sys::dne_pin_pivot_rect(vec2(min), vec2(max)) };
532 }
533
534 pub fn pivot_size(&self, size: [f32; 2]) {
535 assert_non_negative_finite_vec2("PinToken::pivot_size()", "size", size);
536 unsafe { sys::dne_pin_pivot_size(vec2(size)) };
537 }
538
539 pub fn pivot_scale(&self, scale: [f32; 2]) {
540 assert_finite_vec2("PinToken::pivot_scale()", "scale", scale);
541 unsafe { sys::dne_pin_pivot_scale(vec2(scale)) };
542 }
543
544 pub fn pivot_alignment(&self, alignment: [f32; 2]) {
545 assert_finite_vec2("PinToken::pivot_alignment()", "alignment", alignment);
546 unsafe { sys::dne_pin_pivot_alignment(vec2(alignment)) };
547 }
548
549 fn end_inner(&mut self) {
550 if !self.ended {
551 unsafe { sys::dne_end_pin() };
552 self.ended = true;
553 }
554 }
555}
556
557impl Drop for PinToken<'_> {
558 fn drop(&mut self) {
559 self.end_inner();
560 }
561}
562
563pub struct GroupHintToken<'a> {
564 ui: &'a Ui,
565 ended: bool,
566 _scope: PhantomData<&'a ()>,
567}
568
569impl<'a> GroupHintToken<'a> {
570 pub fn min(&self) -> [f32; 2] {
571 from_vec2(unsafe { sys::dne_get_group_min() })
572 }
573
574 pub fn max(&self) -> [f32; 2] {
575 from_vec2(unsafe { sys::dne_get_group_max() })
576 }
577
578 pub fn foreground_draw_list(&self) -> DrawListMut<'_> {
579 let draw_list = unsafe { sys::dne_get_hint_foreground_draw_list() };
580 unsafe { DrawListMut::from_raw_mut(self.ui, draw_list.cast()) }
581 }
582
583 pub fn background_draw_list(&self) -> DrawListMut<'_> {
584 let draw_list = unsafe { sys::dne_get_hint_background_draw_list() };
585 unsafe { DrawListMut::from_raw_mut(self.ui, draw_list.cast()) }
586 }
587
588 pub fn end(mut self) {
589 self.end_inner();
590 }
591
592 fn end_inner(&mut self) {
593 if !self.ended {
594 unsafe { sys::dne_end_group_hint() };
595 self.ended = true;
596 }
597 }
598}
599
600impl Drop for GroupHintToken<'_> {
601 fn drop(&mut self) {
602 self.end_inner();
603 }
604}
605
606pub struct SuspensionToken<'a> {
607 suspended: &'a Cell<bool>,
608 resumed: bool,
609}
610
611impl SuspensionToken<'_> {
612 pub fn resume(mut self) {
613 self.resume_inner();
614 }
615
616 fn resume_inner(&mut self) {
617 if !self.resumed {
618 unsafe { sys::dne_resume() };
619 self.suspended.set(false);
620 self.resumed = true;
621 }
622 }
623}
624
625impl Drop for SuspensionToken<'_> {
626 fn drop(&mut self) {
627 self.resume_inner();
628 }
629}
630
631pub struct CreateSession<'a> {
632 ended: bool,
633 _scope: PhantomData<&'a ()>,
634}
635
636impl CreateSession<'_> {
637 pub fn query_new_link(&self) -> Option<(PinId, PinId)> {
638 let mut start = 0usize;
639 let mut end = 0usize;
640 unsafe { sys::dne_query_new_link(&mut start, &mut end) }
641 .then_some((PinId(start), PinId(end)))
642 }
643
644 pub fn query_new_link_styled(&self, color: [f32; 4], thickness: f32) -> Option<(PinId, PinId)> {
645 assert_finite_vec4("CreateSession::query_new_link_styled()", "color", color);
646 assert_non_negative_finite_f32(
647 "CreateSession::query_new_link_styled()",
648 "thickness",
649 thickness,
650 );
651 let mut start = 0usize;
652 let mut end = 0usize;
653 unsafe { sys::dne_query_new_link_styled(&mut start, &mut end, vec4(color), thickness) }
654 .then_some((PinId(start), PinId(end)))
655 }
656
657 pub fn query_new_node(&self) -> Option<PinId> {
658 let mut pin = 0usize;
659 unsafe { sys::dne_query_new_node(&mut pin) }.then_some(PinId(pin))
660 }
661
662 pub fn query_new_node_styled(&self, color: [f32; 4], thickness: f32) -> Option<PinId> {
663 assert_finite_vec4("CreateSession::query_new_node_styled()", "color", color);
664 assert_non_negative_finite_f32(
665 "CreateSession::query_new_node_styled()",
666 "thickness",
667 thickness,
668 );
669 let mut pin = 0usize;
670 unsafe { sys::dne_query_new_node_styled(&mut pin, vec4(color), thickness) }
671 .then_some(PinId(pin))
672 }
673
674 pub fn accept_new_item(&self) -> bool {
675 unsafe { sys::dne_accept_new_item() }
676 }
677
678 pub fn accept_new_item_styled(&self, color: [f32; 4], thickness: f32) -> bool {
679 assert_finite_vec4("CreateSession::accept_new_item_styled()", "color", color);
680 assert_non_negative_finite_f32(
681 "CreateSession::accept_new_item_styled()",
682 "thickness",
683 thickness,
684 );
685 unsafe { sys::dne_accept_new_item_styled(vec4(color), thickness) }
686 }
687
688 pub fn reject_new_item(&self) {
689 unsafe { sys::dne_reject_new_item() };
690 }
691
692 pub fn reject_new_item_styled(&self, color: [f32; 4], thickness: f32) {
693 assert_finite_vec4("CreateSession::reject_new_item_styled()", "color", color);
694 assert_non_negative_finite_f32(
695 "CreateSession::reject_new_item_styled()",
696 "thickness",
697 thickness,
698 );
699 unsafe { sys::dne_reject_new_item_styled(vec4(color), thickness) };
700 }
701
702 pub fn end(mut self) {
703 self.end_inner();
704 }
705
706 fn end_inner(&mut self) {
707 if !self.ended {
708 unsafe { sys::dne_end_create() };
709 self.ended = true;
710 }
711 }
712}
713
714impl Drop for CreateSession<'_> {
715 fn drop(&mut self) {
716 self.end_inner();
717 }
718}
719
720pub struct DeleteSession<'a> {
721 ended: bool,
722 _scope: PhantomData<&'a ()>,
723}
724
725impl DeleteSession<'_> {
726 pub fn query_deleted_link(&self) -> Option<(LinkId, PinId, PinId)> {
727 let mut link = 0usize;
728 let mut start = 0usize;
729 let mut end = 0usize;
730 unsafe { sys::dne_query_deleted_link(&mut link, &mut start, &mut end) }.then_some((
731 LinkId(link),
732 PinId(start),
733 PinId(end),
734 ))
735 }
736
737 pub fn query_deleted_node(&self) -> Option<NodeId> {
738 let mut node = 0usize;
739 unsafe { sys::dne_query_deleted_node(&mut node) }.then_some(NodeId(node))
740 }
741
742 pub fn accept_deleted_item(&self, delete_dependencies: bool) -> bool {
743 unsafe { sys::dne_accept_deleted_item(delete_dependencies) }
744 }
745
746 pub fn reject_deleted_item(&self) {
747 unsafe { sys::dne_reject_deleted_item() };
748 }
749
750 pub fn end(mut self) {
751 self.end_inner();
752 }
753
754 fn end_inner(&mut self) {
755 if !self.ended {
756 unsafe { sys::dne_end_delete() };
757 self.ended = true;
758 }
759 }
760}
761
762impl Drop for DeleteSession<'_> {
763 fn drop(&mut self) {
764 self.end_inner();
765 }
766}
767
768pub struct ShortcutSession<'a> {
769 ended: bool,
770 _scope: PhantomData<&'a ()>,
771}
772
773impl ShortcutSession<'_> {
774 pub fn accept_cut(&self) -> bool {
775 unsafe { sys::dne_accept_cut() }
776 }
777
778 pub fn accept_copy(&self) -> bool {
779 unsafe { sys::dne_accept_copy() }
780 }
781
782 pub fn accept_paste(&self) -> bool {
783 unsafe { sys::dne_accept_paste() }
784 }
785
786 pub fn accept_duplicate(&self) -> bool {
787 unsafe { sys::dne_accept_duplicate() }
788 }
789
790 pub fn accept_create_node(&self) -> bool {
791 unsafe { sys::dne_accept_create_node() }
792 }
793
794 pub fn action_context_size(&self) -> usize {
795 unsafe { sys::dne_get_action_context_size() }.max(0) as usize
796 }
797
798 pub fn action_context_nodes(&self) -> Vec<NodeId> {
799 let count = self.action_context_size();
800 collect_node_ids(count, |ptr, len| unsafe {
801 sys::dne_get_action_context_nodes(ptr, len)
802 })
803 }
804
805 pub fn action_context_links(&self) -> Vec<LinkId> {
806 let count = self.action_context_size();
807 collect_link_ids(count, |ptr, len| unsafe {
808 sys::dne_get_action_context_links(ptr, len)
809 })
810 }
811
812 pub fn end(mut self) {
813 self.end_inner();
814 }
815
816 fn end_inner(&mut self) {
817 if !self.ended {
818 unsafe { sys::dne_end_shortcut() };
819 self.ended = true;
820 }
821 }
822}
823
824impl Drop for ShortcutSession<'_> {
825 fn drop(&mut self) {
826 self.end_inner();
827 }
828}
829
830pub struct StyleColorToken<'a> {
831 count: i32,
832 popped: bool,
833 _scope: PhantomData<&'a ()>,
834}
835
836impl StyleColorToken<'_> {
837 pub fn pop(mut self) {
838 self.pop_inner();
839 }
840
841 fn pop_inner(&mut self) {
842 if !self.popped {
843 unsafe { sys::dne_pop_style_color(self.count) };
844 self.popped = true;
845 }
846 }
847}
848
849impl Drop for StyleColorToken<'_> {
850 fn drop(&mut self) {
851 self.pop_inner();
852 }
853}
854
855pub struct StyleVarToken<'a> {
856 count: i32,
857 popped: bool,
858 _scope: PhantomData<&'a ()>,
859}
860
861impl StyleVarToken<'_> {
862 pub fn pop(mut self) {
863 self.pop_inner();
864 }
865
866 fn pop_inner(&mut self) {
867 if !self.popped {
868 unsafe { sys::dne_pop_style_var(self.count) };
869 self.popped = true;
870 }
871 }
872}
873
874impl Drop for StyleVarToken<'_> {
875 fn drop(&mut self) {
876 self.pop_inner();
877 }
878}
879
880fn optional_id<T>(make: fn(usize) -> T, f: impl FnOnce(*mut usize) -> bool) -> Option<T> {
881 let mut raw = 0usize;
882 f(&mut raw).then_some(make(raw))
883}
884
885fn collect_node_ids(count: usize, f: impl FnOnce(*mut usize, i32) -> i32) -> Vec<NodeId> {
886 collect_ids(count, f).into_iter().map(NodeId).collect()
887}
888
889fn collect_link_ids(count: usize, f: impl FnOnce(*mut usize, i32) -> i32) -> Vec<LinkId> {
890 collect_ids(count, f).into_iter().map(LinkId).collect()
891}
892
893fn collect_ids(count: usize, f: impl FnOnce(*mut usize, i32) -> i32) -> Vec<usize> {
894 if count == 0 {
895 return Vec::new();
896 }
897 let count = count.min(i32::MAX as usize);
898 let mut values = vec![0usize; count];
899 let written = f(values.as_mut_ptr(), values.len() as i32).max(0) as usize;
900 values.truncate(written.min(count));
901 values
902}
903
904fn mouse_button_from_index(index: sys::ImGuiMouseButton) -> Option<MouseButton> {
905 match index {
906 value if value == MouseButton::Left as i32 => Some(MouseButton::Left),
907 value if value == MouseButton::Right as i32 => Some(MouseButton::Right),
908 value if value == MouseButton::Middle as i32 => Some(MouseButton::Middle),
909 value if value == MouseButton::Extra1 as i32 => Some(MouseButton::Extra1),
910 value if value == MouseButton::Extra2 as i32 => Some(MouseButton::Extra2),
911 _ => None,
912 }
913}
914
915fn assert_style_var_type(caller: &str, var: StyleVar, expected: StyleVarType) {
916 let actual = var.value_type();
917 assert_eq!(
918 actual, expected,
919 "{caller} expected {expected:?} style variable, got {actual:?} for {var:?}"
920 );
921}
922
923fn assert_finite_f32(caller: &str, name: &str, value: f32) {
924 assert!(value.is_finite(), "{caller} {name} must be finite");
925}
926
927fn assert_non_negative_finite_f32(caller: &str, name: &str, value: f32) {
928 assert_finite_f32(caller, name, value);
929 assert!(value >= 0.0, "{caller} {name} must be non-negative");
930}
931
932fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
933 assert!(
934 value.iter().all(|component| component.is_finite()),
935 "{caller} {name} components must be finite"
936 );
937}
938
939fn assert_finite_rect(caller: &str, min: [f32; 2], max: [f32; 2]) {
940 assert_finite_vec2(caller, "min", min);
941 assert_finite_vec2(caller, "max", max);
942 assert!(
943 min[0] <= max[0] && min[1] <= max[1],
944 "{caller} min must not exceed max"
945 );
946}
947
948fn assert_non_negative_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
949 assert_finite_vec2(caller, name, value);
950 assert!(
951 value.iter().all(|component| *component >= 0.0),
952 "{caller} {name} components must be non-negative"
953 );
954}
955
956fn assert_finite_vec4(caller: &str, name: &str, value: [f32; 4]) {
957 assert!(
958 value.iter().all(|component| component.is_finite()),
959 "{caller} {name} components must be finite"
960 );
961}
962
963#[cfg(test)]
964mod tests {
965 use super::*;
966
967 #[test]
968 fn mouse_button_indices_map_known_imgui_buttons() {
969 assert_eq!(mouse_button_from_index(0), Some(MouseButton::Left));
970 assert_eq!(mouse_button_from_index(1), Some(MouseButton::Right));
971 assert_eq!(mouse_button_from_index(2), Some(MouseButton::Middle));
972 assert_eq!(mouse_button_from_index(3), Some(MouseButton::Extra1));
973 assert_eq!(mouse_button_from_index(4), Some(MouseButton::Extra2));
974 assert_eq!(mouse_button_from_index(-1), None);
975 assert_eq!(mouse_button_from_index(99), None);
976 }
977
978 #[test]
979 #[should_panic(expected = "expected Float style variable")]
980 fn style_var_push_rejects_wrong_value_type() {
981 assert_style_var_type("test", StyleVar::NodePadding, StyleVarType::Float);
982 }
983}