use crate::ffi;
use crate::ffi::{Rectangle, Vector2};
use crate::rgui::scratch::{scratch_txt, scratch_txt_two};
use std::ffi::c_char;
pub trait RaylibGuiControls {
#[inline]
fn gui_label(&mut self, bounds: impl Into<Rectangle>, text: impl AsRef<str>) -> bool {
unsafe { ffi::GuiLabel(bounds.into(), scratch_txt(text)) > 0 }
}
#[inline]
fn gui_button(&mut self, bounds: impl Into<Rectangle>, text: impl AsRef<str>) -> bool {
unsafe { ffi::GuiButton(bounds.into(), scratch_txt(text)) > 0 }
}
#[inline]
fn gui_label_button(&mut self, bounds: impl Into<Rectangle>, text: impl AsRef<str>) -> bool {
unsafe { ffi::GuiLabelButton(bounds.into(), scratch_txt(text)) > 0 }
}
#[inline]
fn gui_toggle(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
active: &mut bool,
) -> bool {
unsafe { ffi::GuiToggle(bounds.into(), scratch_txt(text), active) > 0 }
}
#[inline]
fn gui_toggle_group(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
active: &mut i32,
) -> i32 {
unsafe { ffi::GuiToggleGroup(bounds.into(), scratch_txt(text), active) }
}
#[inline]
fn gui_toggle_slider(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
active: &mut i32,
) -> bool {
unsafe { ffi::GuiToggleSlider(bounds.into(), scratch_txt(text), active) > 0 }
}
#[inline]
fn gui_check_box(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
checked: &mut bool,
) -> bool {
unsafe { ffi::GuiCheckBox(bounds.into(), scratch_txt(text), checked) > 0 }
}
#[inline]
fn gui_combo_box(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
active: &mut i32,
) -> i32 {
unsafe { ffi::GuiComboBox(bounds.into(), scratch_txt(text), active) }
}
#[inline]
fn gui_dropdown_box(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
active: &mut i32,
edit_mode: bool,
) -> bool {
unsafe { ffi::GuiDropdownBox(bounds.into(), scratch_txt(text), active, edit_mode) > 0 }
}
#[inline]
fn gui_spinner(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
value: &mut i32,
min_value: i32,
max_value: i32,
edit_mode: bool,
) -> bool {
debug_assert!(
min_value <= max_value,
"gui_spinner: min_value ({}) must be <= max_value ({})",
min_value,
max_value
);
unsafe {
ffi::GuiSpinner(
bounds.into(),
scratch_txt(text),
value,
min_value,
max_value,
edit_mode,
) > 0
}
}
#[inline]
fn gui_value_box(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
value: &mut i32,
min_value: i32,
max_value: i32,
edit_mode: bool,
) -> bool {
debug_assert!(
min_value <= max_value,
"gui_value_box: min_value ({}) must be <= max_value ({})",
min_value,
max_value
);
unsafe {
ffi::GuiValueBox(
bounds.into(),
scratch_txt(text),
value,
min_value,
max_value,
edit_mode,
) > 0
}
}
#[inline]
fn gui_slider(
&mut self,
bounds: impl Into<Rectangle>,
text_left: impl AsRef<str>,
text_right: impl AsRef<str>,
value: &mut f32,
min_value: f32,
max_value: f32,
) -> bool {
debug_assert!(
min_value <= max_value,
"gui_slider: min_value ({}) must be <= max_value ({})",
min_value,
max_value
);
let (l, r) = scratch_txt_two(text_left, text_right);
unsafe { ffi::GuiSlider(bounds.into(), l, r, value, min_value, max_value) > 0 }
}
#[inline]
fn gui_slider_bar(
&mut self,
bounds: impl Into<Rectangle>,
text_left: impl AsRef<str>,
text_right: impl AsRef<str>,
value: &mut f32,
min_value: f32,
max_value: f32,
) -> bool {
debug_assert!(
min_value <= max_value,
"gui_slider_bar: min_value ({}) must be <= max_value ({})",
min_value,
max_value
);
let (l, r) = scratch_txt_two(text_left, text_right);
unsafe { ffi::GuiSliderBar(bounds.into(), l, r, value, min_value, max_value) > 0 }
}
#[inline]
fn gui_progress_bar(
&mut self,
bounds: impl Into<Rectangle>,
text_left: impl AsRef<str>,
text_right: impl AsRef<str>,
value: &mut f32,
min_value: f32,
max_value: f32,
) -> bool {
debug_assert!(
min_value <= max_value,
"gui_progress_bar: min_value ({}) must be <= max_value ({})",
min_value,
max_value
);
let (l, r) = scratch_txt_two(text_left, text_right);
unsafe { ffi::GuiProgressBar(bounds.into(), l, r, value, min_value, max_value) > 0 }
}
#[inline]
fn gui_status_bar(&mut self, bounds: impl Into<Rectangle>, text: impl AsRef<str>) -> bool {
unsafe { ffi::GuiStatusBar(bounds.into(), scratch_txt(text)) > 0 }
}
#[inline]
fn gui_dummy_rec(&mut self, bounds: impl Into<Rectangle>, text: impl AsRef<str>) -> bool {
unsafe { ffi::GuiDummyRec(bounds.into(), scratch_txt(text)) > 0 }
}
#[inline]
fn gui_grid(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
spacing: f32,
subdivs: i32,
) -> (bool, Vector2) {
let mut mouse_cell = Vector2 { x: 0.0, y: 0.0 };
let r = unsafe {
ffi::GuiGrid(
bounds.into(),
scratch_txt(text),
spacing,
subdivs,
&mut mouse_cell,
) > 0
};
(r, mouse_cell)
}
#[inline]
fn gui_text_box(
&mut self,
bounds: impl Into<Rectangle>,
buffer: &mut String,
edit_mode: bool,
) -> bool {
gui_edit_string(buffer, 0, |ptr, cap| unsafe {
ffi::GuiTextBox(bounds.into(), ptr, cap, edit_mode) > 0
})
}
#[inline]
fn gui_value_box_float(
&mut self,
bounds: impl Into<Rectangle>,
text: impl AsRef<str>,
text_value: &mut String,
value: &mut f32,
edit_mode: bool,
) -> bool {
let label = scratch_txt(text);
gui_edit_string(
text_value,
RAYGUI_VALUEBOX_MAX_CHARS + 1,
|ptr, _cap| unsafe {
ffi::GuiValueBoxFloat(bounds.into(), label, ptr, value, edit_mode) > 0
},
)
}
}
pub(crate) const RAYGUI_VALUEBOX_MAX_CHARS: usize = 32;
pub(crate) fn gui_edit_string(
buffer: &mut String,
min_capacity: usize,
call: impl FnOnce(*mut c_char, i32) -> bool,
) -> bool {
let needed = min_capacity.max(buffer.len() + 1);
if buffer.capacity() < needed {
buffer.reserve(needed - buffer.len());
}
let capacity = buffer.capacity();
unsafe {
let v = buffer.as_mut_vec();
for slot in v.spare_capacity_mut() {
slot.write(0);
}
v.set_len(capacity);
}
let ptr = buffer.as_mut_ptr() as *mut c_char;
let res = call(ptr, capacity as i32);
let scanned = unsafe { std::slice::from_raw_parts(buffer.as_ptr(), capacity) };
let len = scanned.iter().position(|&b| b == 0).unwrap_or(capacity);
unsafe { buffer.as_mut_vec().set_len(len) };
res
}