dear_imgui/
utils.rs

1use crate::input::{Key, MouseButton};
2use crate::{StyleColor, sys};
3use bitflags::bitflags;
4
5bitflags! {
6    /// Flags for hovering detection
7    #[repr(transparent)]
8    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9    pub struct HoveredFlags: i32 {
10        /// Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.
11        const NONE = sys::ImGuiHoveredFlags_None as i32;
12        /// IsWindowHovered() only: Return true if any children of the window is hovered
13        const CHILD_WINDOWS = sys::ImGuiHoveredFlags_ChildWindows as i32;
14        /// IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)
15        const ROOT_WINDOW = sys::ImGuiHoveredFlags_RootWindow as i32;
16        /// IsWindowHovered() only: Return true if any window is hovered
17        const ANY_WINDOW = sys::ImGuiHoveredFlags_AnyWindow as i32;
18        /// IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
19        const NO_POPUP_HIERARCHY = sys::ImGuiHoveredFlags_NoPopupHierarchy as i32;
20        /// IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
21        const DOCK_HIERARCHY = sys::ImGuiHoveredFlags_DockHierarchy as i32;
22        /// Return true even if a popup window is normally blocking access to this item/window
23        const ALLOW_WHEN_BLOCKED_BY_POPUP = sys::ImGuiHoveredFlags_AllowWhenBlockedByPopup as i32;
24        /// Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
25        const ALLOW_WHEN_BLOCKED_BY_ACTIVE_ITEM = sys::ImGuiHoveredFlags_AllowWhenBlockedByActiveItem as i32;
26        /// IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window
27        const ALLOW_WHEN_OVERLAPPED = sys::ImGuiHoveredFlags_AllowWhenOverlapped as i32;
28        /// IsItemHovered() only: Return true even if the item is disabled
29        const ALLOW_WHEN_DISABLED = sys::ImGuiHoveredFlags_AllowWhenDisabled as i32;
30        /// IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse.
31        const NO_NAV_OVERRIDE = sys::ImGuiHoveredFlags_NoNavOverride as i32;
32        /// Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.
33        const FOR_TOOLTIP = sys::ImGuiHoveredFlags_ForTooltip as i32;
34        /// Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.
35        const STATIONARY = sys::ImGuiHoveredFlags_Stationary as i32;
36        /// IsItemHovered() only: Return true immediately (default). As opposed to IsItemHovered() returning true only after style.HoverDelayNormal elapsed (~0.30 sec)
37        const DELAY_NONE = sys::ImGuiHoveredFlags_DelayNone as i32;
38        /// IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.10 sec)
39        const DELAY_SHORT = sys::ImGuiHoveredFlags_DelayShort as i32;
40        /// IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.30 sec)
41        const DELAY_NORMAL = sys::ImGuiHoveredFlags_DelayNormal as i32;
42        /// IsItemHovered() only: Disable shared delay system where moving from one item to a neighboring item keeps the previous timer for a short time (standard for tooltips with long delays)
43        const NO_SHARED_DELAY = sys::ImGuiHoveredFlags_NoSharedDelay as i32;
44    }
45}
46
47impl Default for HoveredFlags {
48    fn default() -> Self {
49        HoveredFlags::NONE
50    }
51}
52
53/// Utility functions for Dear ImGui
54impl crate::ui::Ui {
55    // ============================================================================
56    // Item/widget utilities (non-duplicate functions)
57    // ============================================================================
58
59    /// Returns `true` if the last item open state was toggled
60    #[doc(alias = "IsItemToggledOpen")]
61    pub fn is_item_toggled_open(&self) -> bool {
62        unsafe { sys::igIsItemToggledOpen() }
63    }
64
65    /// Returns the upper-left bounding rectangle of the last item (screen space)
66    #[doc(alias = "GetItemRectMin")]
67    pub fn item_rect_min(&self) -> [f32; 2] {
68        unsafe {
69            let mut rect = sys::ImVec2 { x: 0.0, y: 0.0 };
70            sys::igGetItemRectMin(&mut rect);
71            [rect.x, rect.y]
72        }
73    }
74
75    /// Returns the lower-right bounding rectangle of the last item (screen space)
76    #[doc(alias = "GetItemRectMax")]
77    pub fn item_rect_max(&self) -> [f32; 2] {
78        unsafe {
79            let mut rect = sys::ImVec2 { x: 0.0, y: 0.0 };
80            sys::igGetItemRectMax(&mut rect);
81            [rect.x, rect.y]
82        }
83    }
84
85    // ============================================================================
86    // Window utilities
87    // ============================================================================
88
89    /// Returns `true` if the current window is hovered (and typically: not blocked by a popup/modal)
90    #[doc(alias = "IsWindowHovered")]
91    pub fn is_window_hovered(&self) -> bool {
92        unsafe { sys::igIsWindowHovered(HoveredFlags::NONE.bits()) }
93    }
94
95    /// Returns `true` if the current window is hovered based on the given flags
96    #[doc(alias = "IsWindowHovered")]
97    pub fn is_window_hovered_with_flags(&self, flags: HoveredFlags) -> bool {
98        unsafe { sys::igIsWindowHovered(flags.bits()) }
99    }
100
101    /// Returns `true` if the current window is focused (and typically: not blocked by a popup/modal)
102    #[doc(alias = "IsWindowFocused")]
103    pub fn is_window_focused(&self) -> bool {
104        unsafe { sys::igIsWindowFocused(0) }
105    }
106
107    /// Returns `true` if the current window is appearing this frame.
108    #[doc(alias = "IsWindowAppearing")]
109    pub fn is_window_appearing(&self) -> bool {
110        unsafe { sys::igIsWindowAppearing() }
111    }
112
113    /// Returns `true` if the current window is collapsed.
114    #[doc(alias = "IsWindowCollapsed")]
115    pub fn is_window_collapsed(&self) -> bool {
116        unsafe { sys::igIsWindowCollapsed() }
117    }
118
119    // ============================================================================
120    // Additional input utilities (non-duplicate functions)
121    // ============================================================================
122
123    /// Returns the number of times the key was pressed in the current frame
124    #[doc(alias = "GetKeyPressedAmount")]
125    pub fn get_key_pressed_amount(&self, key: Key, repeat_delay: f32, rate: f32) -> i32 {
126        unsafe { sys::igGetKeyPressedAmount(key.into(), repeat_delay, rate) }
127    }
128
129    /// Returns the name of a key
130    #[doc(alias = "GetKeyName")]
131    pub fn get_key_name(&self, key: Key) -> &str {
132        unsafe {
133            let name_ptr = sys::igGetKeyName(key.into());
134            let c_str = std::ffi::CStr::from_ptr(name_ptr);
135            c_str.to_str().unwrap_or("Unknown")
136        }
137    }
138
139    /// Returns the number of times the mouse button was clicked in the current frame
140    #[doc(alias = "GetMouseClickedCount")]
141    pub fn get_mouse_clicked_count(&self, button: MouseButton) -> i32 {
142        unsafe { sys::igGetMouseClickedCount(button.into()) }
143    }
144
145    /// Returns the mouse position in screen coordinates
146    #[doc(alias = "GetMousePos")]
147    pub fn get_mouse_pos(&self) -> [f32; 2] {
148        unsafe {
149            let mut pos = sys::ImVec2 { x: 0.0, y: 0.0 };
150            sys::igGetMousePos(&mut pos);
151            [pos.x, pos.y]
152        }
153    }
154
155    /// Returns the mouse position when the button was clicked
156    #[doc(alias = "GetMousePosOnOpeningCurrentPopup")]
157    pub fn get_mouse_pos_on_opening_current_popup(&self) -> [f32; 2] {
158        unsafe {
159            let mut pos = sys::ImVec2 { x: 0.0, y: 0.0 };
160            sys::igGetMousePosOnOpeningCurrentPopup(&mut pos);
161            [pos.x, pos.y]
162        }
163    }
164
165    /// Returns the mouse drag delta
166    #[doc(alias = "GetMouseDragDelta")]
167    pub fn get_mouse_drag_delta(&self, button: MouseButton, lock_threshold: f32) -> [f32; 2] {
168        unsafe {
169            let mut delta = sys::ImVec2 { x: 0.0, y: 0.0 };
170            sys::igGetMouseDragDelta(&mut delta, button.into(), lock_threshold);
171            [delta.x, delta.y]
172        }
173    }
174
175    /// Returns the mouse wheel delta
176    #[doc(alias = "GetIO")]
177    pub fn get_mouse_wheel(&self) -> f32 {
178        unsafe { (*sys::igGetIO_Nil()).MouseWheel }
179    }
180
181    /// Returns the horizontal mouse wheel delta
182    #[doc(alias = "GetIO")]
183    pub fn get_mouse_wheel_h(&self) -> f32 {
184        unsafe { (*sys::igGetIO_Nil()).MouseWheelH }
185    }
186
187    /// Returns `true` if any mouse button is down
188    #[doc(alias = "IsAnyMouseDown")]
189    pub fn is_any_mouse_down(&self) -> bool {
190        unsafe { sys::igIsAnyMouseDown() }
191    }
192
193    // ============================================================================
194    // General utilities
195    // ============================================================================
196
197    /// Get global imgui time. Incremented by io.DeltaTime every frame.
198    #[doc(alias = "GetTime")]
199    pub fn time(&self) -> f64 {
200        unsafe { sys::igGetTime() }
201    }
202
203    /// Get global imgui frame count. Incremented by 1 every frame.
204    #[doc(alias = "GetFrameCount")]
205    pub fn frame_count(&self) -> i32 {
206        unsafe { sys::igGetFrameCount() }
207    }
208
209    /// Returns a single style color from the user interface style.
210    ///
211    /// Use this function if you need to access the colors, but don't want to clone the entire
212    /// style object.
213    #[doc(alias = "GetStyle")]
214    pub fn style_color(&self, style_color: StyleColor) -> [f32; 4] {
215        unsafe {
216            let style_ptr = sys::igGetStyle();
217            let colors = (*style_ptr).Colors.as_ptr();
218            let color = *colors.add(style_color as usize);
219            [color.x, color.y, color.z, color.w]
220        }
221    }
222
223    /// Returns the name of a style color.
224    ///
225    /// This is just a wrapper around calling [`name`] on [StyleColor].
226    ///
227    /// [`name`]: StyleColor::name
228    #[doc(alias = "GetStyleColorName")]
229    pub fn style_color_name(&self, style_color: StyleColor) -> &'static str {
230        unsafe {
231            let name_ptr = sys::igGetStyleColorName(style_color as sys::ImGuiCol);
232            let c_str = std::ffi::CStr::from_ptr(name_ptr);
233            c_str.to_str().unwrap_or("Unknown")
234        }
235    }
236
237    /// Test if rectangle (of given size, starting from cursor position) is visible / not clipped.
238    #[doc(alias = "IsRectVisible")]
239    pub fn is_rect_visible(&self, size: [f32; 2]) -> bool {
240        unsafe {
241            let size = sys::ImVec2 {
242                x: size[0],
243                y: size[1],
244            };
245            sys::igIsRectVisible_Nil(size)
246        }
247    }
248
249    /// Test if rectangle (in screen space) is visible / not clipped.
250    #[doc(alias = "IsRectVisible")]
251    pub fn is_rect_visible_ex(&self, rect_min: [f32; 2], rect_max: [f32; 2]) -> bool {
252        unsafe {
253            let rect_min = sys::ImVec2 {
254                x: rect_min[0],
255                y: rect_min[1],
256            };
257            let rect_max = sys::ImVec2 {
258                x: rect_max[0],
259                y: rect_max[1],
260            };
261            sys::igIsRectVisible_Vec2(rect_min, rect_max)
262        }
263    }
264
265    // ========== Additional Geometry Functions ==========
266
267    /// Get cursor position in screen coordinates.
268    #[doc(alias = "GetCursorScreenPos")]
269    pub fn get_cursor_screen_pos(&self) -> [f32; 2] {
270        unsafe {
271            let mut pos = sys::ImVec2 { x: 0.0, y: 0.0 };
272            sys::igGetCursorScreenPos(&mut pos);
273            [pos.x, pos.y]
274        }
275    }
276
277    /// Get available content region size.
278    #[doc(alias = "GetContentRegionAvail")]
279    pub fn get_content_region_avail(&self) -> [f32; 2] {
280        unsafe {
281            let mut size = sys::ImVec2 { x: 0.0, y: 0.0 };
282            sys::igGetContentRegionAvail(&mut size);
283            [size.x, size.y]
284        }
285    }
286
287    /// Check if a point is inside a rectangle.
288    pub fn is_point_in_rect(
289        &self,
290        point: [f32; 2],
291        rect_min: [f32; 2],
292        rect_max: [f32; 2],
293    ) -> bool {
294        point[0] >= rect_min[0]
295            && point[0] <= rect_max[0]
296            && point[1] >= rect_min[1]
297            && point[1] <= rect_max[1]
298    }
299
300    /// Calculate distance between two points.
301    pub fn distance(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
302        let dx = p2[0] - p1[0];
303        let dy = p2[1] - p1[1];
304        (dx * dx + dy * dy).sqrt()
305    }
306
307    /// Calculate squared distance between two points (faster than distance).
308    pub fn distance_squared(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
309        let dx = p2[0] - p1[0];
310        let dy = p2[1] - p1[1];
311        dx * dx + dy * dy
312    }
313
314    /// Check if two line segments intersect.
315    pub fn line_segments_intersect(
316        &self,
317        p1: [f32; 2],
318        p2: [f32; 2],
319        p3: [f32; 2],
320        p4: [f32; 2],
321    ) -> bool {
322        let d1 = self.cross_product(
323            [p4[0] - p3[0], p4[1] - p3[1]],
324            [p1[0] - p3[0], p1[1] - p3[1]],
325        );
326        let d2 = self.cross_product(
327            [p4[0] - p3[0], p4[1] - p3[1]],
328            [p2[0] - p3[0], p2[1] - p3[1]],
329        );
330        let d3 = self.cross_product(
331            [p2[0] - p1[0], p2[1] - p1[1]],
332            [p3[0] - p1[0], p3[1] - p1[1]],
333        );
334        let d4 = self.cross_product(
335            [p2[0] - p1[0], p2[1] - p1[1]],
336            [p4[0] - p1[0], p4[1] - p1[1]],
337        );
338
339        (d1 > 0.0) != (d2 > 0.0) && (d3 > 0.0) != (d4 > 0.0)
340    }
341
342    /// Calculate cross product of two 2D vectors.
343    fn cross_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
344        v1[0] * v2[1] - v1[1] * v2[0]
345    }
346
347    /// Normalize a 2D vector.
348    pub fn normalize(&self, v: [f32; 2]) -> [f32; 2] {
349        let len = (v[0] * v[0] + v[1] * v[1]).sqrt();
350        if len > f32::EPSILON {
351            [v[0] / len, v[1] / len]
352        } else {
353            [0.0, 0.0]
354        }
355    }
356
357    /// Calculate dot product of two 2D vectors.
358    pub fn dot_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
359        v1[0] * v2[0] + v1[1] * v2[1]
360    }
361
362    /// Calculate the angle between two vectors in radians.
363    pub fn angle_between_vectors(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
364        let dot = self.dot_product(v1, v2);
365        let len1 = (v1[0] * v1[0] + v1[1] * v1[1]).sqrt();
366        let len2 = (v2[0] * v2[0] + v2[1] * v2[1]).sqrt();
367
368        if len1 > f32::EPSILON && len2 > f32::EPSILON {
369            (dot / (len1 * len2)).acos()
370        } else {
371            0.0
372        }
373    }
374
375    /// Check if a point is inside a circle.
376    pub fn is_point_in_circle(&self, point: [f32; 2], center: [f32; 2], radius: f32) -> bool {
377        self.distance_squared(point, center) <= radius * radius
378    }
379
380    /// Calculate the area of a triangle given three points.
381    pub fn triangle_area(&self, p1: [f32; 2], p2: [f32; 2], p3: [f32; 2]) -> f32 {
382        let cross = self.cross_product(
383            [p2[0] - p1[0], p2[1] - p1[1]],
384            [p3[0] - p1[0], p3[1] - p1[1]],
385        );
386        cross.abs() * 0.5
387    }
388
389    // Additional utility functions
390
391    /// Allows the next item to be overlapped by a subsequent item.
392    #[doc(alias = "SetNextItemAllowOverlap")]
393    pub fn set_next_item_allow_overlap(&self) {
394        unsafe { sys::igSetNextItemAllowOverlap() };
395    }
396}