dear_imgui_rs/widget/
misc.rs1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::as_conversions
10)]
11use crate::Ui;
12use crate::create_token;
13use crate::sys;
14
15fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
16 assert!(
17 value[0].is_finite() && value[1].is_finite(),
18 "{caller} {name} must contain finite values"
19 );
20}
21
22fn validate_invisible_button_flags(caller: &str, flags: ButtonFlags) {
23 let unsupported = flags.bits() & !ButtonFlags::all().bits();
24 assert!(
25 unsupported == 0,
26 "{caller} received unsupported ImGuiButtonFlags bits: 0x{unsupported:X}"
27 );
28}
29
30fn validate_arrow_direction(caller: &str, dir: crate::Direction) {
31 assert!(
32 matches!(
33 dir,
34 crate::Direction::Left
35 | crate::Direction::Right
36 | crate::Direction::Up
37 | crate::Direction::Down
38 ),
39 "{caller} direction must be Left, Right, Up, or Down"
40 );
41}
42
43bitflags::bitflags! {
44 #[repr(transparent)]
46 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47 pub struct ButtonFlags: i32 {
48 const NONE = 0;
50 const ALLOW_OVERLAP = sys::ImGuiButtonFlags_AllowOverlap as i32;
52 const ENABLE_NAV = sys::ImGuiButtonFlags_EnableNav as i32;
54 const MOUSE_BUTTON_LEFT = sys::ImGuiButtonFlags_MouseButtonLeft as i32;
56 const MOUSE_BUTTON_RIGHT = sys::ImGuiButtonFlags_MouseButtonRight as i32;
58 const MOUSE_BUTTON_MIDDLE = sys::ImGuiButtonFlags_MouseButtonMiddle as i32;
60 }
61}
62
63pub use crate::Direction as ArrowDirection;
65
66impl Ui {
67 #[doc(alias = "Bullet")]
69 pub fn bullet(&self) {
70 unsafe {
71 sys::igBullet();
72 }
73 }
74
75 #[doc(alias = "BulletText")]
77 pub fn bullet_text(&self, text: impl AsRef<str>) {
78 let text_ptr = self.scratch_txt(text);
79 unsafe {
80 const FMT: &[u8; 3] = b"%s\0";
82 sys::igBulletText(FMT.as_ptr() as *const std::os::raw::c_char, text_ptr);
83 }
84 }
85}
86
87impl Ui {
88 #[doc(alias = "SmallButton")]
90 pub fn small_button(&self, label: impl AsRef<str>) -> bool {
91 let label_ptr = self.scratch_txt(label);
92 unsafe { sys::igSmallButton(label_ptr) }
93 }
94
95 #[doc(alias = "InvisibleButton")]
97 pub fn invisible_button(&self, str_id: impl AsRef<str>, size: impl Into<[f32; 2]>) -> bool {
98 self.invisible_button_flags(str_id, size, crate::widget::ButtonFlags::NONE)
99 }
100
101 #[doc(alias = "InvisibleButton")]
103 pub fn invisible_button_flags(
104 &self,
105 str_id: impl AsRef<str>,
106 size: impl Into<[f32; 2]>,
107 flags: crate::widget::ButtonFlags,
108 ) -> bool {
109 validate_invisible_button_flags("Ui::invisible_button_flags()", flags);
110 let id_ptr = self.scratch_txt(str_id);
111 let size = size.into();
112 assert_finite_vec2("Ui::invisible_button_flags()", "size", size);
113 let size_vec: sys::ImVec2 = size.into();
114 unsafe { sys::igInvisibleButton(id_ptr, size_vec, flags.bits()) }
115 }
116
117 #[doc(alias = "ArrowButton")]
119 pub fn arrow_button(&self, str_id: impl AsRef<str>, dir: crate::Direction) -> bool {
120 validate_arrow_direction("Ui::arrow_button()", dir);
121 let id_ptr = self.scratch_txt(str_id);
122 unsafe { sys::igArrowButton(id_ptr, dir as i32) }
123 }
124}
125
126#[must_use]
132pub struct DisabledToken<'ui> {
133 _ui: &'ui Ui,
134}
135
136impl<'ui> DisabledToken<'ui> {
137 fn new(ui: &'ui Ui) -> Self {
138 DisabledToken { _ui: ui }
139 }
140
141 pub fn end(self) {
143 }
145}
146
147impl<'ui> Drop for DisabledToken<'ui> {
148 fn drop(&mut self) {
149 unsafe { sys::igEndDisabled() }
150 }
151}
152
153impl Ui {
154 #[doc(alias = "BeginDisabled")]
159 pub fn begin_disabled(&self) -> DisabledToken<'_> {
160 unsafe { sys::igBeginDisabled(true) }
161 DisabledToken::new(self)
162 }
163
164 #[doc(alias = "BeginDisabled")]
169 pub fn begin_disabled_with_cond(&self, disabled: bool) -> DisabledToken<'_> {
170 unsafe { sys::igBeginDisabled(disabled) }
171 DisabledToken::new(self)
172 }
173}
174
175create_token!(
180 pub struct ButtonRepeatToken<'ui>;
182
183 #[doc(alias = "PopButtonRepeat")]
185 drop { unsafe { sys::igPopItemFlag() } }
186);
187
188impl ButtonRepeatToken<'_> {
189 pub fn pop(self) {
191 self.end()
192 }
193}
194
195impl Ui {
196 #[doc(alias = "PushButtonRepeat")]
205 pub fn push_button_repeat(&self, repeat: bool) {
206 unsafe { sys::igPushItemFlag(sys::ImGuiItemFlags_ButtonRepeat as i32, repeat) }
207 }
208
209 #[doc(alias = "PushButtonRepeat")]
211 pub fn push_button_repeat_token(&self, repeat: bool) -> ButtonRepeatToken<'_> {
212 self.push_button_repeat(repeat);
213 ButtonRepeatToken::new(self)
214 }
215
216 #[doc(alias = "PushButtonRepeat", alias = "PopButtonRepeat")]
220 pub fn with_button_repeat<R>(&self, repeat: bool, f: impl FnOnce() -> R) -> R {
221 let _repeat = self.push_button_repeat_token(repeat);
222 f()
223 }
224
225 #[doc(alias = "PopButtonRepeat")]
227 pub fn pop_button_repeat(&self) {
228 unsafe { sys::igPopItemFlag() }
229 }
230}
231
232impl Ui {
237 #[doc(alias = "SetItemKeyOwner")]
241 pub fn set_item_key_owner(&self, key: crate::input::Key) -> bool {
242 let k: sys::ImGuiKey = key as sys::ImGuiKey;
243 unsafe { sys::igSetItemKeyOwner_Nil(k) }
244 }
245
246 #[doc(alias = "SetItemKeyOwner")]
250 pub fn set_item_key_owner_with_flags(
251 &self,
252 key: crate::input::Key,
253 flags: crate::input::ItemKeyOwnerFlags,
254 ) -> bool {
255 let k: sys::ImGuiKey = key as sys::ImGuiKey;
256 unsafe { sys::igSetItemKeyOwner_InputFlags(k, flags.raw()) }
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 fn setup_context() -> crate::Context {
265 let mut ctx = crate::Context::create();
266 let _ = ctx.font_atlas_mut().build();
267 ctx.io_mut().set_display_size([128.0, 128.0]);
268 ctx.io_mut().set_delta_time(1.0 / 60.0);
269 ctx
270 }
271
272 #[test]
273 fn with_button_repeat_pops_after_panic() {
274 let mut ctx = setup_context();
275 let ui = ctx.frame();
276 let raw_ctx = unsafe { sys::igGetCurrentContext() };
277 assert!(!raw_ctx.is_null());
278 let initial_stack_size = unsafe { (*raw_ctx).ItemFlagsStack.Size };
279
280 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
281 ui.with_button_repeat(true, || {
282 assert_eq!(
283 unsafe { (*raw_ctx).ItemFlagsStack.Size },
284 initial_stack_size + 1
285 );
286 panic!("forced panic while button repeat is pushed");
287 });
288 }));
289
290 assert!(result.is_err());
291 assert_eq!(
292 unsafe { (*raw_ctx).ItemFlagsStack.Size },
293 initial_stack_size
294 );
295 }
296}