Skip to main content

dear_imgui_rs/
state_storage.rs

1//! State storage utilities
2//!
3//! Dear ImGui provides a per-window key/value storage (`ImGuiStorage`) that is
4//! used by many widgets and can also be used by custom widgets to persist state.
5//!
6use crate::{Id, sys};
7use std::marker::PhantomData;
8use std::ptr::NonNull;
9
10/// A non-owning reference to an `ImGuiStorage` belonging to the current context.
11#[derive(Copy, Clone, Debug)]
12pub struct StateStorage<'ui> {
13    raw: NonNull<sys::ImGuiStorage>,
14    _phantom: PhantomData<&'ui mut sys::ImGuiStorage>,
15}
16
17impl<'ui> StateStorage<'ui> {
18    /// # Safety
19    /// `raw` must be a valid, non-null pointer to an `ImGuiStorage`.
20    pub unsafe fn from_raw(raw: *mut sys::ImGuiStorage) -> Self {
21        let raw = NonNull::new(raw).expect("StateStorage::from_raw() requires non-null pointer");
22        Self {
23            raw,
24            _phantom: PhantomData,
25        }
26    }
27
28    /// Returns the raw `ImGuiStorage*`.
29    pub fn as_raw(self) -> *mut sys::ImGuiStorage {
30        self.raw.as_ptr()
31    }
32
33    /// Clears all storage entries.
34    pub fn clear(&mut self) {
35        unsafe { sys::ImGuiStorage_Clear(self.raw.as_ptr()) }
36    }
37
38    pub fn get_int(&self, key: Id, default: i32) -> i32 {
39        unsafe { sys::ImGuiStorage_GetInt(self.raw.as_ptr(), key.raw(), default) }
40    }
41
42    pub fn set_int(&mut self, key: Id, value: i32) {
43        unsafe { sys::ImGuiStorage_SetInt(self.raw.as_ptr(), key.raw(), value) }
44    }
45
46    pub fn get_bool(&self, key: Id, default: bool) -> bool {
47        unsafe { sys::ImGuiStorage_GetBool(self.raw.as_ptr(), key.raw(), default) }
48    }
49
50    pub fn set_bool(&mut self, key: Id, value: bool) {
51        unsafe { sys::ImGuiStorage_SetBool(self.raw.as_ptr(), key.raw(), value) }
52    }
53
54    pub fn get_float(&self, key: Id, default: f32) -> f32 {
55        unsafe { sys::ImGuiStorage_GetFloat(self.raw.as_ptr(), key.raw(), default) }
56    }
57
58    pub fn set_float(&mut self, key: Id, value: f32) {
59        unsafe { sys::ImGuiStorage_SetFloat(self.raw.as_ptr(), key.raw(), value) }
60    }
61}
62
63/// Owns an `ImGuiStorage` and clears it on drop.
64///
65/// This is useful when you want to keep widget state outside of the current
66/// window storage (e.g. sharing state across windows or providing custom storage
67/// for a widget subtree via [`crate::Ui::with_state_storage`]).
68#[derive(Debug, Default)]
69pub struct OwnedStateStorage {
70    raw: sys::ImGuiStorage,
71}
72
73impl OwnedStateStorage {
74    pub fn new() -> Self {
75        Self::default()
76    }
77
78    pub fn as_mut(&mut self) -> &mut sys::ImGuiStorage {
79        &mut self.raw
80    }
81
82    pub fn as_ref(&self) -> &sys::ImGuiStorage {
83        &self.raw
84    }
85
86    pub fn as_raw_mut(&mut self) -> *mut sys::ImGuiStorage {
87        &mut self.raw as *mut sys::ImGuiStorage
88    }
89
90    pub fn as_raw(&self) -> *const sys::ImGuiStorage {
91        &self.raw as *const sys::ImGuiStorage
92    }
93}
94
95impl Drop for OwnedStateStorage {
96    fn drop(&mut self) {
97        unsafe { sys::ImGuiStorage_Clear(self.as_raw_mut()) }
98    }
99}
100
101struct StateStorageOverride {
102    previous: *mut sys::ImGuiStorage,
103}
104
105impl Drop for StateStorageOverride {
106    fn drop(&mut self) {
107        unsafe { sys::igSetStateStorage(self.previous) }
108    }
109}
110
111impl crate::ui::Ui {
112    /// Accesses the current window's state storage inside a non-escaping closure.
113    ///
114    /// The storage view cannot outlive this call. The owning context remains current for the
115    /// duration of `f`, including nested calls into other contexts.
116    #[doc(alias = "GetStateStorage")]
117    pub fn with_current_state_storage<R>(
118        &self,
119        f: impl for<'storage> FnOnce(StateStorage<'storage>) -> R,
120    ) -> R {
121        self.run_with_bound_context(|| unsafe {
122            f(StateStorage::from_raw(sys::igGetStateStorage()))
123        })
124    }
125
126    /// Overrides the current state storage while `f` runs.
127    ///
128    /// The owning context remains current throughout the call. Nested overrides restore in LIFO
129    /// order, and restoration also runs if `f` panics. The replacement storage and its scoped view
130    /// cannot escape the closure.
131    ///
132    /// ```compile_fail
133    /// use dear_imgui_rs::{Context, OwnedStateStorage, StateStorage};
134    ///
135    /// let mut context = Context::create();
136    /// let ui = context.frame();
137    /// let mut replacement = OwnedStateStorage::new();
138    /// let escaped: StateStorage<'_> =
139    ///     ui.with_state_storage(&mut replacement, |storage| storage);
140    /// # let _ = escaped;
141    /// ```
142    #[doc(alias = "SetStateStorage")]
143    pub fn with_state_storage<R>(
144        &self,
145        storage: &mut OwnedStateStorage,
146        f: impl for<'storage> FnOnce(StateStorage<'storage>) -> R,
147    ) -> R {
148        self.run_with_bound_context(|| {
149            let replacement = storage.as_raw_mut();
150            let scoped_storage = unsafe { StateStorage::from_raw(replacement) };
151            let previous = unsafe { sys::igGetStateStorage() };
152            unsafe { sys::igSetStateStorage(replacement) };
153            let storage_override = StateStorageOverride { previous };
154            let result = f(scoped_storage);
155            drop(storage_override);
156            result
157        })
158    }
159
160    /// Set the storage ID for the next item.
161    #[doc(alias = "SetNextItemStorageID")]
162    pub fn set_next_item_storage_id(&self, storage_id: Id) {
163        self.run_with_bound_context(|| unsafe { sys::igSetNextItemStorageID(storage_id.raw()) });
164    }
165}