dear_imgui_rs/stacks/
item.rs1use bitflags::bitflags;
2
3use crate::{Ui, sys};
4
5bitflags! {
6 #[repr(transparent)]
8 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9 pub struct ItemFlags: i32 {
10 const NONE = sys::ImGuiItemFlags_None as i32;
12 const NO_TAB_STOP = sys::ImGuiItemFlags_NoTabStop as i32;
14 const NO_NAV = sys::ImGuiItemFlags_NoNav as i32;
16 const NO_NAV_DEFAULT_FOCUS = sys::ImGuiItemFlags_NoNavDefaultFocus as i32;
18 const BUTTON_REPEAT = sys::ImGuiItemFlags_ButtonRepeat as i32;
20 const AUTO_CLOSE_POPUPS = sys::ImGuiItemFlags_AutoClosePopups as i32;
22 const ALLOW_DUPLICATE_ID = sys::ImGuiItemFlags_AllowDuplicateId as i32;
24 const LIVE_EDIT_ON_INPUT_TEXT = sys::ImGuiItemFlags_LiveEditOnInputText as i32;
26 const LIVE_EDIT_ON_INPUT_SCALAR = sys::ImGuiItemFlags_LiveEditOnInputScalar as i32;
28 const LIVE_EDIT_ON_INPUT = sys::ImGuiItemFlags_LiveEditOnInput as i32;
30 }
31}
32
33bitflags! {
34 #[repr(transparent)]
40 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41 pub struct ItemStateFlags: i32 {
42 const NONE = sys::ImGuiItemFlags_None as i32;
44 const NO_TAB_STOP = sys::ImGuiItemFlags_NoTabStop as i32;
46 const NO_NAV = sys::ImGuiItemFlags_NoNav as i32;
48 const NO_NAV_DEFAULT_FOCUS = sys::ImGuiItemFlags_NoNavDefaultFocus as i32;
50 const BUTTON_REPEAT = sys::ImGuiItemFlags_ButtonRepeat as i32;
52 const AUTO_CLOSE_POPUPS = sys::ImGuiItemFlags_AutoClosePopups as i32;
54 const ALLOW_DUPLICATE_ID = sys::ImGuiItemFlags_AllowDuplicateId as i32;
56 const LIVE_EDIT_ON_INPUT_TEXT = sys::ImGuiItemFlags_LiveEditOnInputText as i32;
58 const LIVE_EDIT_ON_INPUT_SCALAR = sys::ImGuiItemFlags_LiveEditOnInputScalar as i32;
60 const LIVE_EDIT_ON_INPUT = sys::ImGuiItemFlags_LiveEditOnInput as i32;
62 const DISABLED = sys::ImGuiItemFlags_Disabled as i32;
64 }
65}
66
67impl Default for ItemFlags {
68 fn default() -> Self {
69 Self::NONE
70 }
71}
72
73impl Default for ItemStateFlags {
74 fn default() -> Self {
75 Self::NONE
76 }
77}
78
79impl From<ItemFlags> for ItemStateFlags {
80 fn from(flags: ItemFlags) -> Self {
81 Self::from_bits_retain(flags.bits())
82 }
83}
84
85create_token!(
86 pub struct ItemFlagStackToken<'ui>;
88
89 pop crate::scope::NativeScopePop::PopItemFlag;
90
91 #[doc(alias = "PopItemFlag")]
93 drop { unsafe { sys::igPopItemFlag() } }
94);
95
96impl ItemFlagStackToken<'_> {
97 pub fn pop(self) {
103 self.end()
104 }
105}
106
107impl Ui {
108 #[doc(alias = "GetItemFlags")]
112 pub fn item_flags(&self) -> ItemStateFlags {
113 self.run_with_bound_context(|| unsafe {
114 ItemStateFlags::from_bits_retain(sys::igGetItemFlags())
115 })
116 }
117
118 #[doc(alias = "PushItemFlag")]
122 pub fn push_item_flag(&self, flags: ItemFlags, enabled: bool) -> ItemFlagStackToken<'_> {
123 self.run_with_bound_context(|| unsafe { sys::igPushItemFlag(flags.bits(), enabled) });
124 ItemFlagStackToken::new(self)
125 }
126
127 #[doc(alias = "PushItemFlag", alias = "PopItemFlag")]
131 pub fn with_item_flag<R>(&self, flags: ItemFlags, enabled: bool, f: impl FnOnce() -> R) -> R {
132 let flags = self.push_item_flag(flags, enabled);
133 let result = f();
134 drop(flags);
135 result
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 fn setup_context() -> crate::Context {
144 let mut ctx = crate::Context::create();
145 ctx.io_mut().set_display_size([128.0, 128.0]);
146 ctx.io_mut().set_delta_time(1.0 / 60.0);
147 ctx.font_atlas()
148 .try_claim_legacy_renderer()
149 .expect("legacy renderer font atlas should be available")
150 .build();
151 ctx
152 }
153
154 #[test]
155 fn item_flag_scope_is_typed_and_restores_previous_flags() {
156 let mut ctx = setup_context();
157 let ui = ctx.frame();
158
159 ui.window("item_flags").build(|| {
160 ui.with_item_flag(
161 ItemFlags::NO_NAV
162 | ItemFlags::ALLOW_DUPLICATE_ID
163 | ItemFlags::LIVE_EDIT_ON_INPUT_SCALAR,
164 true,
165 || {
166 ui.button("scoped");
167 let flags = ui.item_flags();
168 assert!(flags.contains(ItemStateFlags::NO_NAV));
169 assert!(flags.contains(ItemStateFlags::ALLOW_DUPLICATE_ID));
170 assert!(flags.contains(ItemStateFlags::LIVE_EDIT_ON_INPUT_SCALAR));
171 },
172 );
173
174 ui.button("restored");
175 let flags = ui.item_flags();
176 assert!(!flags.contains(ItemStateFlags::NO_NAV));
177 assert!(!flags.contains(ItemStateFlags::ALLOW_DUPLICATE_ID));
178 assert!(!flags.contains(ItemStateFlags::LIVE_EDIT_ON_INPUT_SCALAR));
179 });
180 }
181
182 #[test]
183 fn item_flag_scope_can_clear_defaults_and_reports_disabled_items() {
184 let mut ctx = setup_context();
185 let ui = ctx.frame();
186
187 ui.window("item_flag_values").build(|| {
188 ui.button("default");
189 assert!(ui.item_flags().contains(ItemStateFlags::AUTO_CLOSE_POPUPS));
190
191 {
192 let _flags = ui.push_item_flag(ItemFlags::AUTO_CLOSE_POPUPS, false);
193 ui.button("without_auto_close");
194 assert!(!ui.item_flags().contains(ItemStateFlags::AUTO_CLOSE_POPUPS));
195 }
196
197 ui.button("restored_default");
198 assert!(ui.item_flags().contains(ItemStateFlags::AUTO_CLOSE_POPUPS));
199
200 {
201 let _disabled = ui.begin_disabled();
202 ui.button("disabled");
203 assert!(ui.item_flags().contains(ItemStateFlags::DISABLED));
204 }
205 });
206 }
207
208 #[test]
209 fn disabled_and_item_flag_tokens_share_one_strict_native_stack() {
210 let mut ctx = setup_context();
211 let ui = ctx.frame();
212
213 ui.window("item_flag_ordering").build(|| {
214 let flag = ui.push_item_flag(ItemFlags::NO_NAV, true);
215 let disabled = ui.begin_disabled();
216
217 assert!(std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| drop(flag))).is_err());
218 assert!(
219 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
220 ui.button("blocked while recovery is pending");
221 }))
222 .is_err()
223 );
224
225 drop(disabled);
226 ui.button("recovered");
227 assert!(!ui.item_flags().contains(ItemStateFlags::NO_NAV));
228 assert!(!ui.item_flags().contains(ItemStateFlags::DISABLED));
229 });
230 }
231
232 #[test]
233 fn closure_scope_drops_its_return_value_before_recovering_an_outer_token() {
234 let mut ctx = setup_context();
235 let ui = ctx.frame();
236
237 ui.window("item_flag_closure_ordering").build(|| {
238 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
239 let _ = ui.with_item_flag(ItemFlags::NO_NAV, true, || {
240 ui.push_item_flag(ItemFlags::BUTTON_REPEAT, true)
241 });
242 }));
243 assert!(result.is_err());
244
245 ui.button("recovered after closure result cleanup");
246 let flags = ui.item_flags();
247 assert!(!flags.contains(ItemStateFlags::NO_NAV));
248 assert!(!flags.contains(ItemStateFlags::BUTTON_REPEAT));
249 });
250 }
251}