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