dear_imnodes/context/
global.rs1use super::{Context, EditorContext, ImNodesScope};
2use crate::sys;
3use dear_imgui_rs::Context as ImGuiContext;
4use dear_imgui_rs::sys as imgui_sys;
5use std::marker::PhantomData;
6use std::os::raw::c_char;
7use std::ptr::NonNull;
8
9pub struct BoundEditor<'a> {
14 scope: ImNodesScope,
15 _ctx: &'a Context,
16 _editor: &'a EditorContext,
17}
18
19impl<'a> BoundEditor<'a> {
20 #[inline]
21 fn bind(&self) {
22 self.scope.bind();
23 }
24
25 #[inline]
26 pub fn set_current(&self) {
27 self.bind();
28 }
29
30 pub fn get_panning(&self) -> [f32; 2] {
31 self.bind();
32 let out = unsafe { crate::compat_ffi::imnodes_EditorContextGetPanning() };
33 [out.x, out.y]
34 }
35
36 pub fn reset_panning(&self, pos: [f32; 2]) {
37 self.bind();
38 unsafe {
39 sys::imnodes_EditorContextResetPanning(sys::ImVec2_c {
40 x: pos[0],
41 y: pos[1],
42 })
43 };
44 }
45
46 pub fn move_to_node(&self, node_id: i32) {
47 self.bind();
48 unsafe { sys::imnodes_EditorContextMoveToNode(node_id) };
49 }
50
51 pub fn save_state_to_ini_string(&self) -> String {
53 self.bind();
54 unsafe {
55 let mut size: usize = 0;
56 let ptr =
57 sys::imnodes_SaveEditorStateToIniString(self._editor.raw, &mut size as *mut usize);
58 if ptr.is_null() || size == 0 {
59 return String::new();
60 }
61 let mut slice = std::slice::from_raw_parts(ptr as *const u8, size);
62 if slice.last() == Some(&0) {
63 slice = &slice[..slice.len().saturating_sub(1)];
64 }
65 String::from_utf8_lossy(slice).into_owned()
66 }
67 }
68
69 pub fn load_state_from_ini_string(&self, data: &str) {
71 self.bind();
72 unsafe {
73 sys::imnodes_LoadEditorStateFromIniString(
74 self._editor.raw,
75 data.as_ptr() as *const c_char,
76 data.len(),
77 )
78 }
79 }
80
81 pub fn save_state_to_ini_file(&self, file_name: &str) {
83 self.bind();
84 let file_name = if file_name.contains('\0') {
85 ""
86 } else {
87 file_name
88 };
89 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
90 sys::imnodes_SaveEditorStateToIniFile(self._editor.raw, ptr)
91 })
92 }
93
94 pub fn load_state_from_ini_file(&self, file_name: &str) {
96 self.bind();
97 let file_name = if file_name.contains('\0') {
98 ""
99 } else {
100 file_name
101 };
102 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
103 sys::imnodes_LoadEditorStateFromIniFile(self._editor.raw, ptr)
104 })
105 }
106}
107
108impl Context {
109 pub fn try_create(imgui: &ImGuiContext) -> dear_imgui_rs::ImGuiResult<Self> {
111 let imgui_ctx_raw = imgui.as_raw();
112 let imgui_alive = imgui.alive_token();
113 unsafe { sys::imnodes_SetImGuiContext(imgui_ctx_raw) };
114 let raw = unsafe { sys::imnodes_CreateContext() };
115 if raw.is_null() {
116 return Err(dear_imgui_rs::ImGuiError::context_creation(
117 "imnodes_CreateContext returned null",
118 ));
119 }
120 unsafe { sys::imnodes_SetCurrentContext(raw) };
121 Ok(Self {
122 raw,
123 imgui_ctx_raw,
124 imgui_alive,
125 _not_send_sync: PhantomData,
126 })
127 }
128
129 pub fn create(imgui: &ImGuiContext) -> Self {
131 Self::try_create(imgui).expect("Failed to create ImNodes context")
132 }
133
134 pub fn set_as_current(&self) {
136 assert!(
137 self.imgui_alive.is_alive(),
138 "dear-imnodes: ImGui context has been dropped"
139 );
140 unsafe { sys::imnodes_SetImGuiContext(self.imgui_ctx_raw) };
141 unsafe { sys::imnodes_SetCurrentContext(self.raw) };
142 }
143
144 pub fn as_raw(&self) -> *mut sys::ImNodesContext {
146 self.raw
147 }
148
149 pub fn imgui_context_raw(&self) -> *mut imgui_sys::ImGuiContext {
151 self.imgui_ctx_raw
152 }
153
154 pub fn current_raw() -> Option<NonNull<sys::ImNodesContext>> {
156 let ptr = unsafe { sys::imnodes_GetCurrentContext() };
157 NonNull::new(ptr)
158 }
159
160 pub fn bind_editor<'a>(&'a self, editor: &'a EditorContext) -> BoundEditor<'a> {
162 if let Some(bound) = editor.bound_ctx_raw {
163 assert_eq!(
164 bound, self.raw,
165 "dear-imnodes: EditorContext is bound to a different ImNodes context"
166 );
167 }
168 let scope = ImNodesScope {
169 imgui_ctx_raw: self.imgui_ctx_raw,
170 imgui_alive: self.imgui_alive.clone(),
171 ctx_raw: self.raw,
172 editor_raw: Some(editor.raw),
173 };
174 BoundEditor {
175 scope,
176 _ctx: self,
177 _editor: editor,
178 }
179 }
180
181 pub fn try_create_editor_context(&self) -> dear_imgui_rs::ImGuiResult<EditorContext> {
182 assert!(
183 self.imgui_alive.is_alive(),
184 "dear-imnodes: ImGui context has been dropped"
185 );
186 unsafe {
187 sys::imnodes_SetImGuiContext(self.imgui_ctx_raw);
188 sys::imnodes_SetCurrentContext(self.raw);
189 }
190 let raw = unsafe { sys::imnodes_EditorContextCreate() };
191 if raw.is_null() {
192 return Err(dear_imgui_rs::ImGuiError::context_creation(
193 "imnodes_EditorContextCreate returned null",
194 ));
195 }
196 Ok(EditorContext {
197 raw,
198 bound_ctx_raw: Some(self.raw),
199 bound_imgui_ctx_raw: Some(self.imgui_ctx_raw),
200 bound_imgui_alive: Some(self.imgui_alive.clone()),
201 _not_send_sync: PhantomData,
202 })
203 }
204
205 pub fn create_editor_context(&self) -> EditorContext {
206 self.try_create_editor_context()
207 .expect("Failed to create ImNodes editor context")
208 }
209}
210
211impl Drop for Context {
212 fn drop(&mut self) {
213 if !self.raw.is_null() {
214 if self.imgui_alive.is_alive() {
215 unsafe {
216 sys::imnodes_SetImGuiContext(self.imgui_ctx_raw);
217 if sys::imnodes_GetCurrentContext() == self.raw {
218 sys::imnodes_SetCurrentContext(std::ptr::null_mut());
219 }
220 }
221 } else {
222 unsafe {
225 if sys::imnodes_GetCurrentContext() == self.raw {
226 sys::imnodes_SetCurrentContext(std::ptr::null_mut());
227 }
228 }
229 }
230 unsafe { sys::imnodes_DestroyContext(self.raw) };
231 }
232 }
233}
234
235impl EditorContext {
238 #[inline]
239 fn bind_current(&self) {
240 let imgui_ctx_raw = unsafe { imgui_sys::igGetCurrentContext() };
241 assert!(
242 !imgui_ctx_raw.is_null(),
243 "dear-imnodes: EditorContext methods require an active ImGui context"
244 );
245 unsafe { sys::imnodes_SetImGuiContext(imgui_ctx_raw) };
246 assert!(
247 !unsafe { sys::imnodes_GetCurrentContext() }.is_null(),
248 "dear-imnodes: EditorContext methods require an active ImNodes context (call Context::set_as_current or use NodesUi)"
249 );
250 unsafe { sys::imnodes_EditorContextSet(self.raw) };
251 }
252
253 #[deprecated(
254 note = "Deprecated: will be removed in 0.11.0. Use `ctx.try_create_editor_context()`."
255 )]
256 pub fn try_create() -> dear_imgui_rs::ImGuiResult<Self> {
257 let raw = unsafe { sys::imnodes_EditorContextCreate() };
258 if raw.is_null() {
259 return Err(dear_imgui_rs::ImGuiError::context_creation(
260 "imnodes_EditorContextCreate returned null",
261 ));
262 }
263 Ok(Self {
264 raw,
265 bound_ctx_raw: None,
266 bound_imgui_ctx_raw: None,
267 bound_imgui_alive: None,
268 _not_send_sync: PhantomData,
269 })
270 }
271
272 #[deprecated(
273 note = "Deprecated: will be removed in 0.11.0. Use `ctx.create_editor_context()`."
274 )]
275 pub fn create() -> Self {
276 #[allow(deprecated)]
277 {
278 Self::try_create().expect("Failed to create ImNodes editor context")
279 }
280 }
281
282 #[deprecated(
283 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).set_current()`."
284 )]
285 pub fn set_current(&self) {
286 self.bind_current();
287 }
288
289 #[deprecated(
290 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).get_panning()`."
291 )]
292 pub fn get_panning(&self) -> [f32; 2] {
293 self.bind_current();
294 let out = unsafe { crate::compat_ffi::imnodes_EditorContextGetPanning() };
295 [out.x, out.y]
296 }
297
298 #[deprecated(
299 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).reset_panning(...)`."
300 )]
301 pub fn reset_panning(&self, pos: [f32; 2]) {
302 self.bind_current();
303 unsafe {
304 sys::imnodes_EditorContextResetPanning(sys::ImVec2_c {
305 x: pos[0],
306 y: pos[1],
307 })
308 };
309 }
310
311 #[deprecated(
312 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).move_to_node(...)`."
313 )]
314 pub fn move_to_node(&self, node_id: i32) {
315 self.bind_current();
316 unsafe { sys::imnodes_EditorContextMoveToNode(node_id) };
317 }
318
319 #[deprecated(
321 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).save_state_to_ini_string()`."
322 )]
323 pub fn save_state_to_ini_string(&self) -> String {
324 self.bind_current();
325 unsafe {
326 let mut size: usize = 0;
327 let ptr = sys::imnodes_SaveEditorStateToIniString(self.raw, &mut size as *mut usize);
328 if ptr.is_null() || size == 0 {
329 return String::new();
330 }
331 let mut slice = std::slice::from_raw_parts(ptr as *const u8, size);
332 if slice.last() == Some(&0) {
333 slice = &slice[..slice.len().saturating_sub(1)];
334 }
335 String::from_utf8_lossy(slice).into_owned()
336 }
337 }
338
339 #[deprecated(
341 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).load_state_from_ini_string(...)`."
342 )]
343 pub fn load_state_from_ini_string(&self, data: &str) {
344 self.bind_current();
345 unsafe {
346 sys::imnodes_LoadEditorStateFromIniString(
347 self.raw,
348 data.as_ptr() as *const c_char,
349 data.len(),
350 )
351 }
352 }
353
354 #[deprecated(
356 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).save_state_to_ini_file(...)`."
357 )]
358 pub fn save_state_to_ini_file(&self, file_name: &str) {
359 self.bind_current();
360 let file_name = if file_name.contains('\0') {
361 ""
362 } else {
363 file_name
364 };
365 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
366 sys::imnodes_SaveEditorStateToIniFile(self.raw, ptr)
367 })
368 }
369
370 #[deprecated(
372 note = "Deprecated: will be removed in 0.11.0. Use `ctx.bind_editor(&editor).load_state_from_ini_file(...)`."
373 )]
374 pub fn load_state_from_ini_file(&self, file_name: &str) {
375 self.bind_current();
376 let file_name = if file_name.contains('\0') {
377 ""
378 } else {
379 file_name
380 };
381 dear_imgui_rs::with_scratch_txt(file_name, |ptr| unsafe {
382 sys::imnodes_LoadEditorStateFromIniFile(self.raw, ptr)
383 })
384 }
385}
386
387impl Drop for EditorContext {
388 fn drop(&mut self) {
389 if !self.raw.is_null() {
390 if let Some(alive) = &self.bound_imgui_alive {
391 if !alive.is_alive() {
392 return;
395 }
396 }
397 if let Some(imgui_ctx_raw) = self.bound_imgui_ctx_raw {
398 unsafe { sys::imnodes_SetImGuiContext(imgui_ctx_raw) };
399 }
400 if let Some(ctx_raw) = self.bound_ctx_raw {
401 unsafe { sys::imnodes_SetCurrentContext(ctx_raw) };
402 }
403 unsafe { sys::imnodes_EditorContextFree(self.raw) };
404 }
405 }
406}
407
408