Skip to main content

dear_node_editor/
frame.rs

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
8/// RAII token for an active node-editor frame.
9pub 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}