use crate::input::{Key, MouseButton};
use crate::{StyleColor, sys};
use bitflags::bitflags;
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HoveredFlags: i32 {
const NONE = sys::ImGuiHoveredFlags_None as i32;
const CHILD_WINDOWS = sys::ImGuiHoveredFlags_ChildWindows as i32;
const ROOT_WINDOW = sys::ImGuiHoveredFlags_RootWindow as i32;
const ANY_WINDOW = sys::ImGuiHoveredFlags_AnyWindow as i32;
const NO_POPUP_HIERARCHY = sys::ImGuiHoveredFlags_NoPopupHierarchy as i32;
const DOCK_HIERARCHY = sys::ImGuiHoveredFlags_DockHierarchy as i32;
const ALLOW_WHEN_BLOCKED_BY_POPUP = sys::ImGuiHoveredFlags_AllowWhenBlockedByPopup as i32;
const ALLOW_WHEN_BLOCKED_BY_ACTIVE_ITEM = sys::ImGuiHoveredFlags_AllowWhenBlockedByActiveItem as i32;
const ALLOW_WHEN_OVERLAPPED_BY_ITEM = sys::ImGuiHoveredFlags_AllowWhenOverlappedByItem as i32;
const ALLOW_WHEN_OVERLAPPED_BY_WINDOW = sys::ImGuiHoveredFlags_AllowWhenOverlappedByWindow as i32;
const ALLOW_WHEN_OVERLAPPED = sys::ImGuiHoveredFlags_AllowWhenOverlapped as i32;
const ALLOW_WHEN_DISABLED = sys::ImGuiHoveredFlags_AllowWhenDisabled as i32;
const NO_NAV_OVERRIDE = sys::ImGuiHoveredFlags_NoNavOverride as i32;
const RECT_ONLY = sys::ImGuiHoveredFlags_RectOnly as i32;
const ROOT_AND_CHILD_WINDOWS = sys::ImGuiHoveredFlags_RootAndChildWindows as i32;
const FOR_TOOLTIP = sys::ImGuiHoveredFlags_ForTooltip as i32;
const STATIONARY = sys::ImGuiHoveredFlags_Stationary as i32;
const DELAY_NONE = sys::ImGuiHoveredFlags_DelayNone as i32;
const DELAY_SHORT = sys::ImGuiHoveredFlags_DelayShort as i32;
const DELAY_NORMAL = sys::ImGuiHoveredFlags_DelayNormal as i32;
const NO_SHARED_DELAY = sys::ImGuiHoveredFlags_NoSharedDelay as i32;
}
}
impl Default for HoveredFlags {
fn default() -> Self {
HoveredFlags::NONE
}
}
bitflags! {
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FocusedFlags: i32 {
const NONE = sys::ImGuiFocusedFlags_None as i32;
const CHILD_WINDOWS = sys::ImGuiFocusedFlags_ChildWindows as i32;
const ROOT_WINDOW = sys::ImGuiFocusedFlags_RootWindow as i32;
const ANY_WINDOW = sys::ImGuiFocusedFlags_AnyWindow as i32;
const NO_POPUP_HIERARCHY = sys::ImGuiFocusedFlags_NoPopupHierarchy as i32;
const DOCK_HIERARCHY = sys::ImGuiFocusedFlags_DockHierarchy as i32;
const ROOT_AND_CHILD_WINDOWS = sys::ImGuiFocusedFlags_RootAndChildWindows as i32;
}
}
impl Default for FocusedFlags {
fn default() -> Self {
FocusedFlags::NONE
}
}
const HOVERED_FLAGS_ALLOWED_FOR_WINDOW: i32 =
sys::ImGuiHoveredFlags_AllowedMaskForIsWindowHovered as i32;
const HOVERED_FLAGS_ALLOWED_FOR_ITEM: i32 =
sys::ImGuiHoveredFlags_AllowedMaskForIsItemHovered as i32;
fn validate_hovered_flags(caller: &str, flags: HoveredFlags, allowed_mask: i32) {
let unsupported = flags.bits() & !allowed_mask;
assert!(
unsupported == 0,
"{caller} received ImGuiHoveredFlags bits that are invalid for this call: 0x{unsupported:X}"
);
}
pub(crate) fn validate_window_hovered_flags(caller: &str, flags: HoveredFlags) {
validate_hovered_flags(caller, flags, HOVERED_FLAGS_ALLOWED_FOR_WINDOW);
}
pub(crate) fn validate_item_hovered_flags(caller: &str, flags: HoveredFlags) {
validate_hovered_flags(caller, flags, HOVERED_FLAGS_ALLOWED_FOR_ITEM);
}
pub(crate) fn validate_tooltip_hovered_flags(caller: &str, flags: HoveredFlags) {
validate_item_hovered_flags(caller, flags);
assert!(
!flags.contains(HoveredFlags::FOR_TOOLTIP),
"{caller} stores the flags expanded by ImGuiHoveredFlags_ForTooltip, so FOR_TOOLTIP itself is not valid here"
);
}
fn validate_focused_flags(caller: &str, flags: FocusedFlags) {
let unsupported = flags.bits() & !FocusedFlags::all().bits();
assert!(
unsupported == 0,
"{caller} received unsupported ImGuiFocusedFlags bits: 0x{unsupported:X}"
);
}
fn assert_finite_f32(caller: &str, name: &str, value: f32) {
assert!(value.is_finite(), "{caller} {name} must be finite");
}
fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
assert!(
value[0].is_finite() && value[1].is_finite(),
"{caller} {name} must contain finite values"
);
}
fn assert_finite_vec4(caller: &str, name: &str, value: [f32; 4]) {
assert!(
value.iter().all(|component| component.is_finite()),
"{caller} {name} must contain finite values"
);
}
impl crate::ui::Ui {
#[doc(alias = "IsItemToggledOpen")]
pub fn is_item_toggled_open(&self) -> bool {
unsafe { sys::igIsItemToggledOpen() }
}
#[doc(alias = "GetItemRectMin")]
pub fn item_rect_min(&self) -> [f32; 2] {
let rect = unsafe { sys::igGetItemRectMin() };
[rect.x, rect.y]
}
#[doc(alias = "GetItemRectMax")]
pub fn item_rect_max(&self) -> [f32; 2] {
let rect = unsafe { sys::igGetItemRectMax() };
[rect.x, rect.y]
}
#[doc(alias = "IsWindowHovered")]
pub fn is_window_hovered(&self) -> bool {
unsafe { sys::igIsWindowHovered(HoveredFlags::NONE.bits()) }
}
#[doc(alias = "IsWindowHovered")]
pub fn is_window_hovered_with_flags(&self, flags: HoveredFlags) -> bool {
validate_window_hovered_flags("Ui::is_window_hovered_with_flags()", flags);
unsafe { sys::igIsWindowHovered(flags.bits()) }
}
#[doc(alias = "IsWindowFocused")]
pub fn is_window_focused(&self) -> bool {
self.is_window_focused_with_flags(FocusedFlags::NONE)
}
#[doc(alias = "IsWindowFocused")]
pub fn is_window_focused_with_flags(&self, flags: FocusedFlags) -> bool {
validate_focused_flags("Ui::is_window_focused_with_flags()", flags);
unsafe { sys::igIsWindowFocused(flags.bits()) }
}
#[doc(alias = "IsWindowAppearing")]
pub fn is_window_appearing(&self) -> bool {
unsafe { sys::igIsWindowAppearing() }
}
#[doc(alias = "IsWindowCollapsed")]
pub fn is_window_collapsed(&self) -> bool {
unsafe { sys::igIsWindowCollapsed() }
}
#[doc(alias = "GetKeyPressedAmount")]
pub fn get_key_pressed_amount(&self, key: Key, repeat_delay: f32, rate: f32) -> i32 {
assert_finite_f32("Ui::get_key_pressed_amount()", "repeat_delay", repeat_delay);
assert_finite_f32("Ui::get_key_pressed_amount()", "rate", rate);
unsafe { sys::igGetKeyPressedAmount(key.into(), repeat_delay, rate) }
}
#[doc(alias = "GetKeyName")]
pub fn get_key_name(&self, key: Key) -> &str {
unsafe {
let name_ptr = sys::igGetKeyName(key.into());
if name_ptr.is_null() {
return "Unknown";
}
let c_str = std::ffi::CStr::from_ptr(name_ptr);
c_str.to_str().unwrap_or("Unknown")
}
}
#[doc(alias = "GetMouseClickedCount")]
pub fn get_mouse_clicked_count(&self, button: MouseButton) -> i32 {
unsafe { sys::igGetMouseClickedCount(button.into()) }
}
#[doc(alias = "GetMousePos")]
pub fn get_mouse_pos(&self) -> [f32; 2] {
let pos = unsafe { sys::igGetMousePos() };
[pos.x, pos.y]
}
#[doc(alias = "GetMousePosOnOpeningCurrentPopup")]
pub fn get_mouse_pos_on_opening_current_popup(&self) -> [f32; 2] {
let pos = unsafe { sys::igGetMousePosOnOpeningCurrentPopup() };
[pos.x, pos.y]
}
#[doc(alias = "GetMouseDragDelta")]
pub fn get_mouse_drag_delta(&self, button: MouseButton, lock_threshold: f32) -> [f32; 2] {
assert_finite_f32(
"Ui::get_mouse_drag_delta()",
"lock_threshold",
lock_threshold,
);
let delta = unsafe { sys::igGetMouseDragDelta(button.into(), lock_threshold) };
[delta.x, delta.y]
}
#[doc(alias = "GetIO")]
pub fn get_mouse_wheel(&self) -> f32 {
self.io().mouse_wheel()
}
#[doc(alias = "GetIO")]
pub fn get_mouse_wheel_h(&self) -> f32 {
self.io().mouse_wheel_h()
}
#[doc(alias = "IsAnyMouseDown")]
pub fn is_any_mouse_down(&self) -> bool {
unsafe { sys::igIsAnyMouseDown() }
}
#[doc(alias = "GetTime")]
pub fn time(&self) -> f64 {
unsafe { sys::igGetTime() }
}
#[doc(alias = "GetFrameCount")]
pub fn frame_count(&self) -> i32 {
unsafe { sys::igGetFrameCount() }
}
#[doc(alias = "CalcItemWidth")]
pub fn calc_item_width(&self) -> f32 {
unsafe { sys::igCalcItemWidth() }
}
#[doc(alias = "LogToTTY")]
pub fn log_to_tty(&self, auto_open_depth: i32) {
unsafe { sys::igLogToTTY(auto_open_depth) }
}
#[doc(alias = "LogToFile")]
pub fn log_to_file_default(&self, auto_open_depth: i32) {
unsafe { sys::igLogToFile(auto_open_depth, std::ptr::null()) }
}
#[doc(alias = "LogToFile")]
pub fn log_to_file(
&self,
auto_open_depth: i32,
filename: &std::path::Path,
) -> crate::error::ImGuiResult<()> {
use crate::error::SafeStringConversion;
let cstr = filename.to_string_lossy().into_owned().to_cstring_safe()?;
unsafe { sys::igLogToFile(auto_open_depth, cstr.as_ptr()) }
Ok(())
}
#[doc(alias = "LogToClipboard")]
pub fn log_to_clipboard(&self, auto_open_depth: i32) {
unsafe { sys::igLogToClipboard(auto_open_depth) }
}
#[doc(alias = "LogButtons")]
pub fn log_buttons(&self) {
unsafe { sys::igLogButtons() }
}
#[doc(alias = "LogFinish")]
pub fn log_finish(&self) {
unsafe { sys::igLogFinish() }
}
#[doc(alias = "GetStyle", alias = "GetStyleColorVec4")]
pub fn style_color(&self, style_color: StyleColor) -> [f32; 4] {
unsafe {
let color = sys::igGetStyleColorVec4(style_color as sys::ImGuiCol);
let color = &*color;
[color.x, color.y, color.z, color.w]
}
}
#[doc(alias = "GetColorU32")]
pub fn get_color_u32(&self, style_color: StyleColor) -> u32 {
self.get_color_u32_with_alpha(style_color, 1.0)
}
#[doc(alias = "GetColorU32")]
pub fn get_color_u32_with_alpha(&self, style_color: StyleColor, alpha_mul: f32) -> u32 {
assert_finite_f32("Ui::get_color_u32_with_alpha()", "alpha_mul", alpha_mul);
unsafe { sys::igGetColorU32_Col(style_color as sys::ImGuiCol, alpha_mul) }
}
#[doc(alias = "GetColorU32")]
pub fn get_color_u32_from_rgba(&self, rgba: [f32; 4]) -> u32 {
assert_finite_vec4("Ui::get_color_u32_from_rgba()", "rgba", rgba);
unsafe {
sys::igGetColorU32_Vec4(sys::ImVec4_c {
x: rgba[0],
y: rgba[1],
z: rgba[2],
w: rgba[3],
})
}
}
#[doc(alias = "GetColorU32")]
pub fn get_color_u32_from_packed(&self, abgr: u32, alpha_mul: f32) -> u32 {
assert_finite_f32("Ui::get_color_u32_from_packed()", "alpha_mul", alpha_mul);
unsafe { sys::igGetColorU32_U32(abgr, alpha_mul) }
}
#[doc(alias = "GetStyleColorName")]
pub fn style_color_name(&self, style_color: StyleColor) -> &'static str {
unsafe {
let name_ptr = sys::igGetStyleColorName(style_color as sys::ImGuiCol);
if name_ptr.is_null() {
return "Unknown";
}
let c_str = std::ffi::CStr::from_ptr(name_ptr);
c_str.to_str().unwrap_or("Unknown")
}
}
#[doc(alias = "IsRectVisible")]
pub fn is_rect_visible(&self, size: [f32; 2]) -> bool {
assert_finite_vec2("Ui::is_rect_visible()", "size", size);
unsafe {
let size = sys::ImVec2 {
x: size[0],
y: size[1],
};
sys::igIsRectVisible_Nil(size)
}
}
#[doc(alias = "IsRectVisible")]
pub fn is_rect_visible_ex(&self, rect_min: [f32; 2], rect_max: [f32; 2]) -> bool {
assert_finite_vec2("Ui::is_rect_visible_ex()", "rect_min", rect_min);
assert_finite_vec2("Ui::is_rect_visible_ex()", "rect_max", rect_max);
unsafe {
let rect_min = sys::ImVec2 {
x: rect_min[0],
y: rect_min[1],
};
let rect_max = sys::ImVec2 {
x: rect_max[0],
y: rect_max[1],
};
sys::igIsRectVisible_Vec2(rect_min, rect_max)
}
}
#[doc(alias = "GetCursorScreenPos")]
pub fn get_cursor_screen_pos(&self) -> [f32; 2] {
let pos = unsafe { sys::igGetCursorScreenPos() };
[pos.x, pos.y]
}
#[doc(alias = "GetContentRegionAvail")]
pub fn get_content_region_avail(&self) -> [f32; 2] {
let size = unsafe { sys::igGetContentRegionAvail() };
[size.x, size.y]
}
pub fn is_point_in_rect(
&self,
point: [f32; 2],
rect_min: [f32; 2],
rect_max: [f32; 2],
) -> bool {
point[0] >= rect_min[0]
&& point[0] <= rect_max[0]
&& point[1] >= rect_min[1]
&& point[1] <= rect_max[1]
}
pub fn distance(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
let dx = p2[0] - p1[0];
let dy = p2[1] - p1[1];
(dx * dx + dy * dy).sqrt()
}
pub fn distance_squared(&self, p1: [f32; 2], p2: [f32; 2]) -> f32 {
let dx = p2[0] - p1[0];
let dy = p2[1] - p1[1];
dx * dx + dy * dy
}
pub fn line_segments_intersect(
&self,
p1: [f32; 2],
p2: [f32; 2],
p3: [f32; 2],
p4: [f32; 2],
) -> bool {
let d1 = self.cross_product(
[p4[0] - p3[0], p4[1] - p3[1]],
[p1[0] - p3[0], p1[1] - p3[1]],
);
let d2 = self.cross_product(
[p4[0] - p3[0], p4[1] - p3[1]],
[p2[0] - p3[0], p2[1] - p3[1]],
);
let d3 = self.cross_product(
[p2[0] - p1[0], p2[1] - p1[1]],
[p3[0] - p1[0], p3[1] - p1[1]],
);
let d4 = self.cross_product(
[p2[0] - p1[0], p2[1] - p1[1]],
[p4[0] - p1[0], p4[1] - p1[1]],
);
(d1 > 0.0) != (d2 > 0.0) && (d3 > 0.0) != (d4 > 0.0)
}
fn cross_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
v1[0] * v2[1] - v1[1] * v2[0]
}
pub fn normalize(&self, v: [f32; 2]) -> [f32; 2] {
let len = (v[0] * v[0] + v[1] * v[1]).sqrt();
if len > f32::EPSILON {
[v[0] / len, v[1] / len]
} else {
[0.0, 0.0]
}
}
pub fn dot_product(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
v1[0] * v2[0] + v1[1] * v2[1]
}
pub fn angle_between_vectors(&self, v1: [f32; 2], v2: [f32; 2]) -> f32 {
let dot = self.dot_product(v1, v2);
let len1 = (v1[0] * v1[0] + v1[1] * v1[1]).sqrt();
let len2 = (v2[0] * v2[0] + v2[1] * v2[1]).sqrt();
if len1 > f32::EPSILON && len2 > f32::EPSILON {
(dot / (len1 * len2)).acos()
} else {
0.0
}
}
pub fn is_point_in_circle(&self, point: [f32; 2], center: [f32; 2], radius: f32) -> bool {
self.distance_squared(point, center) <= radius * radius
}
pub fn triangle_area(&self, p1: [f32; 2], p2: [f32; 2], p3: [f32; 2]) -> f32 {
let cross = self.cross_product(
[p2[0] - p1[0], p2[1] - p1[1]],
[p3[0] - p1[0], p3[1] - p1[1]],
);
cross.abs() * 0.5
}
#[doc(alias = "SetNextItemAllowOverlap")]
pub fn set_next_item_allow_overlap(&self) {
unsafe { sys::igSetNextItemAllowOverlap() };
}
}