1use crate::input::{Key, MouseButton};
8use crate::{StyleColor, sys};
9use bitflags::bitflags;
10
11bitflags! {
12 #[repr(transparent)]
14 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15 pub struct HoveredFlags: i32 {
16 const NONE = sys::ImGuiHoveredFlags_None as i32;
18 const CHILD_WINDOWS = sys::ImGuiHoveredFlags_ChildWindows as i32;
20 const ROOT_WINDOW = sys::ImGuiHoveredFlags_RootWindow as i32;
22 const ANY_WINDOW = sys::ImGuiHoveredFlags_AnyWindow as i32;
24 const NO_POPUP_HIERARCHY = sys::ImGuiHoveredFlags_NoPopupHierarchy as i32;
26 const DOCK_HIERARCHY = sys::ImGuiHoveredFlags_DockHierarchy as i32;
28 const ALLOW_WHEN_BLOCKED_BY_POPUP = sys::ImGuiHoveredFlags_AllowWhenBlockedByPopup as i32;
30 const ALLOW_WHEN_BLOCKED_BY_ACTIVE_ITEM = sys::ImGuiHoveredFlags_AllowWhenBlockedByActiveItem as i32;
32 const ALLOW_WHEN_OVERLAPPED_BY_ITEM = sys::ImGuiHoveredFlags_AllowWhenOverlappedByItem as i32;
34 const ALLOW_WHEN_OVERLAPPED_BY_WINDOW = sys::ImGuiHoveredFlags_AllowWhenOverlappedByWindow as i32;
36 const ALLOW_WHEN_OVERLAPPED = sys::ImGuiHoveredFlags_AllowWhenOverlapped as i32;
38 const ALLOW_WHEN_DISABLED = sys::ImGuiHoveredFlags_AllowWhenDisabled as i32;
40 const NO_NAV_OVERRIDE = sys::ImGuiHoveredFlags_NoNavOverride as i32;
42 const RECT_ONLY = sys::ImGuiHoveredFlags_RectOnly as i32;
44 const ROOT_AND_CHILD_WINDOWS = sys::ImGuiHoveredFlags_RootAndChildWindows as i32;
46 const FOR_TOOLTIP = sys::ImGuiHoveredFlags_ForTooltip as i32;
48 const STATIONARY = sys::ImGuiHoveredFlags_Stationary as i32;
50 const DELAY_NONE = sys::ImGuiHoveredFlags_DelayNone as i32;
52 const DELAY_SHORT = sys::ImGuiHoveredFlags_DelayShort as i32;
54 const DELAY_NORMAL = sys::ImGuiHoveredFlags_DelayNormal as i32;
56 const NO_SHARED_DELAY = sys::ImGuiHoveredFlags_NoSharedDelay as i32;
58 }
59}
60
61impl Default for HoveredFlags {
62 fn default() -> Self {
63 HoveredFlags::NONE
64 }
65}
66
67bitflags! {
68 #[repr(transparent)]
70 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
71 pub struct FocusedFlags: i32 {
72 const NONE = sys::ImGuiFocusedFlags_None as i32;
74 const CHILD_WINDOWS = sys::ImGuiFocusedFlags_ChildWindows as i32;
76 const ROOT_WINDOW = sys::ImGuiFocusedFlags_RootWindow as i32;
78 const ANY_WINDOW = sys::ImGuiFocusedFlags_AnyWindow as i32;
80 const NO_POPUP_HIERARCHY = sys::ImGuiFocusedFlags_NoPopupHierarchy as i32;
82 const DOCK_HIERARCHY = sys::ImGuiFocusedFlags_DockHierarchy as i32;
84 const ROOT_AND_CHILD_WINDOWS = sys::ImGuiFocusedFlags_RootAndChildWindows as i32;
86 }
87}
88
89impl Default for FocusedFlags {
90 fn default() -> Self {
91 FocusedFlags::NONE
92 }
93}
94
95const HOVERED_FLAGS_ALLOWED_FOR_WINDOW: i32 =
96 sys::ImGuiHoveredFlags_AllowedMaskForIsWindowHovered as i32;
97const HOVERED_FLAGS_ALLOWED_FOR_ITEM: i32 =
98 sys::ImGuiHoveredFlags_AllowedMaskForIsItemHovered as i32;
99
100fn validate_hovered_flags(caller: &str, flags: HoveredFlags, allowed_mask: i32) {
101 let unsupported = flags.bits() & !allowed_mask;
102 assert!(
103 unsupported == 0,
104 "{caller} received ImGuiHoveredFlags bits that are invalid for this call: 0x{unsupported:X}"
105 );
106}
107
108pub(crate) fn validate_window_hovered_flags(caller: &str, flags: HoveredFlags) {
109 validate_hovered_flags(caller, flags, HOVERED_FLAGS_ALLOWED_FOR_WINDOW);
110}
111
112pub(crate) fn validate_item_hovered_flags(caller: &str, flags: HoveredFlags) {
113 validate_hovered_flags(caller, flags, HOVERED_FLAGS_ALLOWED_FOR_ITEM);
114}
115
116pub(crate) fn validate_tooltip_hovered_flags(caller: &str, flags: HoveredFlags) {
117 validate_item_hovered_flags(caller, flags);
118 assert!(
119 !flags.contains(HoveredFlags::FOR_TOOLTIP),
120 "{caller} stores the flags expanded by ImGuiHoveredFlags_ForTooltip, so FOR_TOOLTIP itself is not valid here"
121 );
122}
123
124fn validate_focused_flags(caller: &str, flags: FocusedFlags) {
125 let unsupported = flags.bits() & !FocusedFlags::all().bits();
126 assert!(
127 unsupported == 0,
128 "{caller} received unsupported ImGuiFocusedFlags bits: 0x{unsupported:X}"
129 );
130}
131
132fn assert_finite_f32(caller: &str, name: &str, value: f32) {
133 assert!(value.is_finite(), "{caller} {name} must be finite");
134}
135
136fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
137 assert!(
138 value[0].is_finite() && value[1].is_finite(),
139 "{caller} {name} must contain finite values"
140 );
141}
142
143fn assert_finite_vec4(caller: &str, name: &str, value: [f32; 4]) {
144 assert!(
145 value.iter().all(|component| component.is_finite()),
146 "{caller} {name} must contain finite values"
147 );
148}
149
150impl crate::ui::Ui {
152 #[doc(alias = "IsItemToggledOpen")]
158 pub fn is_item_toggled_open(&self) -> bool {
159 unsafe { sys::igIsItemToggledOpen() }
160 }
161
162 #[doc(alias = "GetItemRectMin")]
164 pub fn item_rect_min(&self) -> [f32; 2] {
165 let rect = unsafe { sys::igGetItemRectMin() };
166 [rect.x, rect.y]
167 }
168
169 #[doc(alias = "GetItemRectMax")]
171 pub fn item_rect_max(&self) -> [f32; 2] {
172 let rect = unsafe { sys::igGetItemRectMax() };
173 [rect.x, rect.y]
174 }
175
176 #[doc(alias = "IsWindowHovered")]
182 pub fn is_window_hovered(&self) -> bool {
183 unsafe { sys::igIsWindowHovered(HoveredFlags::NONE.bits()) }
184 }
185
186 #[doc(alias = "IsWindowHovered")]
188 pub fn is_window_hovered_with_flags(&self, flags: HoveredFlags) -> bool {
189 validate_window_hovered_flags("Ui::is_window_hovered_with_flags()", flags);
190 unsafe { sys::igIsWindowHovered(flags.bits()) }
191 }
192
193 #[doc(alias = "IsWindowFocused")]
195 pub fn is_window_focused(&self) -> bool {
196 self.is_window_focused_with_flags(FocusedFlags::NONE)
197 }
198
199 #[doc(alias = "IsWindowFocused")]
201 pub fn is_window_focused_with_flags(&self, flags: FocusedFlags) -> bool {
202 validate_focused_flags("Ui::is_window_focused_with_flags()", flags);
203 unsafe { sys::igIsWindowFocused(flags.bits()) }
204 }
205
206 #[doc(alias = "IsWindowAppearing")]
208 pub fn is_window_appearing(&self) -> bool {
209 unsafe { sys::igIsWindowAppearing() }
210 }
211
212 #[doc(alias = "IsWindowCollapsed")]
214 pub fn is_window_collapsed(&self) -> bool {
215 unsafe { sys::igIsWindowCollapsed() }
216 }
217
218 #[doc(alias = "GetKeyPressedAmount")]
224 pub fn get_key_pressed_amount(&self, key: Key, repeat_delay: f32, rate: f32) -> i32 {
225 assert_finite_f32("Ui::get_key_pressed_amount()", "repeat_delay", repeat_delay);
226 assert_finite_f32("Ui::get_key_pressed_amount()", "rate", rate);
227 unsafe { sys::igGetKeyPressedAmount(key.into(), repeat_delay, rate) }
228 }
229
230 #[doc(alias = "GetKeyName")]
232 pub fn get_key_name(&self, key: Key) -> &str {
233 unsafe {
234 let name_ptr = sys::igGetKeyName(key.into());
235 if name_ptr.is_null() {
236 return "Unknown";
237 }
238 let c_str = std::ffi::CStr::from_ptr(name_ptr);
239 c_str.to_str().unwrap_or("Unknown")
240 }
241 }
242
243 #[doc(alias = "GetMouseClickedCount")]
245 pub fn get_mouse_clicked_count(&self, button: MouseButton) -> i32 {
246 unsafe { sys::igGetMouseClickedCount(button.into()) }
247 }
248
249 #[doc(alias = "GetMousePos")]
251 pub fn get_mouse_pos(&self) -> [f32; 2] {
252 let pos = unsafe { sys::igGetMousePos() };
253 [pos.x, pos.y]
254 }
255
256 #[doc(alias = "GetMousePosOnOpeningCurrentPopup")]
258 pub fn get_mouse_pos_on_opening_current_popup(&self) -> [f32; 2] {
259 let pos = unsafe { sys::igGetMousePosOnOpeningCurrentPopup() };
260 [pos.x, pos.y]
261 }
262
263 #[doc(alias = "GetMouseDragDelta")]
265 pub fn get_mouse_drag_delta(&self, button: MouseButton, lock_threshold: f32) -> [f32; 2] {
266 assert_finite_f32(
267 "Ui::get_mouse_drag_delta()",
268 "lock_threshold",
269 lock_threshold,
270 );
271 let delta = unsafe { sys::igGetMouseDragDelta(button.into(), lock_threshold) };
272 [delta.x, delta.y]
273 }
274
275 #[doc(alias = "GetIO")]
277 pub fn get_mouse_wheel(&self) -> f32 {
278 self.io().mouse_wheel()
279 }
280
281 #[doc(alias = "GetIO")]
283 pub fn get_mouse_wheel_h(&self) -> f32 {
284 self.io().mouse_wheel_h()
285 }
286
287 #[doc(alias = "IsAnyMouseDown")]
289 pub fn is_any_mouse_down(&self) -> bool {
290 unsafe { sys::igIsAnyMouseDown() }
291 }
292
293 #[doc(alias = "GetTime")]
299 pub fn time(&self) -> f64 {
300 unsafe { sys::igGetTime() }
301 }
302
303 #[doc(alias = "GetFrameCount")]
305 pub fn frame_count(&self) -> i32 {
306 unsafe { sys::igGetFrameCount() }
307 }
308
309 #[doc(alias = "CalcItemWidth")]
311 pub fn calc_item_width(&self) -> f32 {
312 unsafe { sys::igCalcItemWidth() }
313 }
314
315 #[doc(alias = "LogToTTY")]
317 pub fn log_to_tty(&self, auto_open_depth: i32) {
318 unsafe { sys::igLogToTTY(auto_open_depth) }
319 }
320
321 #[doc(alias = "LogToFile")]
323 pub fn log_to_file_default(&self, auto_open_depth: i32) {
324 unsafe { sys::igLogToFile(auto_open_depth, std::ptr::null()) }
325 }
326
327 #[doc(alias = "LogToFile")]
333 pub fn log_to_file(
334 &self,
335 auto_open_depth: i32,
336 filename: &std::path::Path,
337 ) -> crate::error::ImGuiResult<()> {
338 use crate::error::SafeStringConversion;
339 let cstr = filename.to_string_lossy().into_owned().to_cstring_safe()?;
340 unsafe { sys::igLogToFile(auto_open_depth, cstr.as_ptr()) }
341 Ok(())
342 }
343
344 #[doc(alias = "LogToClipboard")]
346 pub fn log_to_clipboard(&self, auto_open_depth: i32) {
347 unsafe { sys::igLogToClipboard(auto_open_depth) }
348 }
349
350 #[doc(alias = "LogButtons")]
352 pub fn log_buttons(&self) {
353 unsafe { sys::igLogButtons() }
354 }
355
356 #[doc(alias = "LogFinish")]
358 pub fn log_finish(&self) {
359 unsafe { sys::igLogFinish() }
360 }
361
362 #[doc(alias = "GetStyle", alias = "GetStyleColorVec4")]
367 pub fn style_color(&self, style_color: StyleColor) -> [f32; 4] {
368 unsafe {
369 let color = sys::igGetStyleColorVec4(style_color as sys::ImGuiCol);
370 let color = &*color;
371 [color.x, color.y, color.z, color.w]
372 }
373 }
374
375 #[doc(alias = "GetColorU32")]
379 pub fn get_color_u32(&self, style_color: StyleColor) -> u32 {
380 self.get_color_u32_with_alpha(style_color, 1.0)
381 }
382
383 #[doc(alias = "GetColorU32")]
385 pub fn get_color_u32_with_alpha(&self, style_color: StyleColor, alpha_mul: f32) -> u32 {
386 assert_finite_f32("Ui::get_color_u32_with_alpha()", "alpha_mul", alpha_mul);
387 unsafe { sys::igGetColorU32_Col(style_color as sys::ImGuiCol, alpha_mul) }
388 }
389
390 #[doc(alias = "GetColorU32")]
394 pub fn get_color_u32_from_rgba(&self, rgba: [f32; 4]) -> u32 {
395 assert_finite_vec4("Ui::get_color_u32_from_rgba()", "rgba", rgba);
396 unsafe {
397 sys::igGetColorU32_Vec4(sys::ImVec4_c {
398 x: rgba[0],
399 y: rgba[1],
400 z: rgba[2],
401 w: rgba[3],
402 })
403 }
404 }
405
406 #[doc(alias = "GetColorU32")]
408 pub fn get_color_u32_from_packed(&self, abgr: u32, alpha_mul: f32) -> u32 {
409 assert_finite_f32("Ui::get_color_u32_from_packed()", "alpha_mul", alpha_mul);
410 unsafe { sys::igGetColorU32_U32(abgr, alpha_mul) }
411 }
412
413 #[doc(alias = "GetStyleColorName")]
419 pub fn style_color_name(&self, style_color: StyleColor) -> &'static str {
420 unsafe {
421 let name_ptr = sys::igGetStyleColorName(style_color as sys::ImGuiCol);
422 if name_ptr.is_null() {
423 return "Unknown";
424 }
425 let c_str = std::ffi::CStr::from_ptr(name_ptr);
426 c_str.to_str().unwrap_or("Unknown")
427 }
428 }
429
430 #[doc(alias = "IsRectVisible")]
432 pub fn is_rect_visible(&self, size: [f32; 2]) -> bool {
433 assert_finite_vec2("Ui::is_rect_visible()", "size", size);
434 unsafe {
435 let size = sys::ImVec2 {
436 x: size[0],
437 y: size[1],
438 };
439 sys::igIsRectVisible_Nil(size)
440 }
441 }
442
443 #[doc(alias = "IsRectVisible")]
445 pub fn is_rect_visible_ex(&self, rect_min: [f32; 2], rect_max: [f32; 2]) -> bool {
446 assert_finite_vec2("Ui::is_rect_visible_ex()", "rect_min", rect_min);
447 assert_finite_vec2("Ui::is_rect_visible_ex()", "rect_max", rect_max);
448 unsafe {
449 let rect_min = sys::ImVec2 {
450 x: rect_min[0],
451 y: rect_min[1],
452 };
453 let rect_max = sys::ImVec2 {
454 x: rect_max[0],
455 y: rect_max[1],
456 };
457 sys::igIsRectVisible_Vec2(rect_min, rect_max)
458 }
459 }
460
461 #[doc(alias = "GetCursorScreenPos")]
465 pub fn get_cursor_screen_pos(&self) -> [f32; 2] {
466 let pos = unsafe { sys::igGetCursorScreenPos() };
467 [pos.x, pos.y]
468 }
469
470 #[doc(alias = "GetContentRegionAvail")]
472 pub fn get_content_region_avail(&self) -> [f32; 2] {
473 let size = unsafe { sys::igGetContentRegionAvail() };
474 [size.x, size.y]
475 }
476
477 pub fn is_point_in_rect(
479 &self,
480 point: [f32; 2],
481 rect_min: [f32; 2],
482 rect_max: [f32; 2],
483 ) -> bool {
484 point[0] >= rect_min[0]
485 && point[0] <= rect_max[0]
486 && point[1] >= rect_min[1]
487 && point[1] <= rect_max[1]
488 }
489
490 pub fn distance(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
492 let dx = p2[0] - p1[0];
493 let dy = p2[1] - p1[1];
494 (dx * dx + dy * dy).sqrt()
495 }
496
497 pub fn distance_squared(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
499 let dx = p2[0] - p1[0];
500 let dy = p2[1] - p1[1];
501 dx * dx + dy * dy
502 }
503
504 pub fn line_segments_intersect(
506 &self,
507 p1: [f32; 2],
508 p2: [f32; 2],
509 p3: [f32; 2],
510 p4: [f32; 2],
511 ) -> bool {
512 let d1 = self.cross_product(
513 [p4[0] - p3[0], p4[1] - p3[1]],
514 [p1[0] - p3[0], p1[1] - p3[1]],
515 );
516 let d2 = self.cross_product(
517 [p4[0] - p3[0], p4[1] - p3[1]],
518 [p2[0] - p3[0], p2[1] - p3[1]],
519 );
520 let d3 = self.cross_product(
521 [p2[0] - p1[0], p2[1] - p1[1]],
522 [p3[0] - p1[0], p3[1] - p1[1]],
523 );
524 let d4 = self.cross_product(
525 [p2[0] - p1[0], p2[1] - p1[1]],
526 [p4[0] - p1[0], p4[1] - p1[1]],
527 );
528
529 (d1 > 0.0) != (d2 > 0.0) && (d3 > 0.0) != (d4 > 0.0)
530 }
531
532 fn cross_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
534 v1[0] * v2[1] - v1[1] * v2[0]
535 }
536
537 pub fn normalize(&self, v: [f32; 2]) -> [f32; 2] {
539 let len = (v[0] * v[0] + v[1] * v[1]).sqrt();
540 if len > f32::EPSILON {
541 [v[0] / len, v[1] / len]
542 } else {
543 [0.0, 0.0]
544 }
545 }
546
547 pub fn dot_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
549 v1[0] * v2[0] + v1[1] * v2[1]
550 }
551
552 pub fn angle_between_vectors(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
554 let dot = self.dot_product(v1, v2);
555 let len1 = (v1[0] * v1[0] + v1[1] * v1[1]).sqrt();
556 let len2 = (v2[0] * v2[0] + v2[1] * v2[1]).sqrt();
557
558 if len1 > f32::EPSILON && len2 > f32::EPSILON {
559 (dot / (len1 * len2)).acos()
560 } else {
561 0.0
562 }
563 }
564
565 pub fn is_point_in_circle(&self, point: [f32; 2], center: [f32; 2], radius: f32) -> bool {
567 self.distance_squared(point, center) <= radius * radius
568 }
569
570 pub fn triangle_area(&self, p1: [f32; 2], p2: [f32; 2], p3: [f32; 2]) -> f32 {
572 let cross = self.cross_product(
573 [p2[0] - p1[0], p2[1] - p1[1]],
574 [p3[0] - p1[0], p3[1] - p1[1]],
575 );
576 cross.abs() * 0.5
577 }
578
579 #[doc(alias = "SetNextItemAllowOverlap")]
583 pub fn set_next_item_allow_overlap(&self) {
584 unsafe { sys::igSetNextItemAllowOverlap() };
585 }
586}