use bitflags::bitflags; use std::marker::PhantomData; use std::os::raw::{c_int, c_void}; use std::ptr; use crate::sys; use crate::{ImStr, ImString, Ui}; bitflags!( /// Flags for text inputs #[repr(C)] pub struct InputTextFlags: u32 { /// Allow 0123456789.+-*/ const CHARS_DECIMAL = sys::ImGuiInputTextFlags_CharsDecimal; /// Allow 0123456789ABCDEFabcdef const CHARS_HEXADECIMAL = sys::ImGuiInputTextFlags_CharsHexadecimal; /// Turn a..z into A..Z const CHARS_UPPERCASE = sys::ImGuiInputTextFlags_CharsUppercase; /// Filter out spaces, tabs const CHARS_NO_BLANK = sys::ImGuiInputTextFlags_CharsNoBlank; /// Select entire text when first taking mouse focus const AUTO_SELECT_ALL = sys::ImGuiInputTextFlags_AutoSelectAll; /// Return 'true' when Enter is pressed (as opposed to when the value was modified) const ENTER_RETURNS_TRUE = sys::ImGuiInputTextFlags_EnterReturnsTrue; /// Call user function on pressing TAB (for completion handling) const CALLBACK_COMPLETION = sys::ImGuiInputTextFlags_CallbackCompletion; /// Call user function on pressing Up/Down arrows (for history handling) const CALLBACK_HISTORY = sys::ImGuiInputTextFlags_CallbackHistory; /// Call user function every time. User code may query cursor position, modify text buffer. const CALLBACK_ALWAYS = sys::ImGuiInputTextFlags_CallbackAlways; /// Call user function to filter character. const CALLBACK_CHAR_FILTER = sys::ImGuiInputTextFlags_CallbackCharFilter; /// Pressing TAB input a '\t' character into the text field const ALLOW_TAB_INPUT = sys::ImGuiInputTextFlags_AllowTabInput; /// In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is /// opposite: unfocus with Ctrl+Enter, add line with Enter). const CTRL_ENTER_FOR_NEW_LINE = sys::ImGuiInputTextFlags_CtrlEnterForNewLine; /// Disable following the cursor horizontally const NO_HORIZONTAL_SCROLL = sys::ImGuiInputTextFlags_NoHorizontalScroll; /// Insert mode const ALWAYS_INSERT_MODE = sys::ImGuiInputTextFlags_AlwaysInsertMode; /// Read-only mode const READ_ONLY = sys::ImGuiInputTextFlags_ReadOnly; /// Password mode, display all characters as '*' const PASSWORD = sys::ImGuiInputTextFlags_Password; /// Disable undo/redo. const NO_UNDO_REDO = sys::ImGuiInputTextFlags_NoUndoRedo; /// Allow 0123456789.+-*/eE (Scientific notation input) const CHARS_SCIENTIFIC = sys::ImGuiInputTextFlags_CharsScientific; /// Allow buffer capacity resize + notify when the string wants to be resized const CALLBACK_RESIZE = sys::ImGuiInputTextFlags_CallbackResize; } ); macro_rules! impl_text_flags { ($InputType:ident) => { #[inline] pub fn flags(mut self, flags: InputTextFlags) -> Self { self.flags = flags; self } #[inline] pub fn chars_decimal(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CHARS_DECIMAL, value); self } #[inline] pub fn chars_hexadecimal(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CHARS_HEXADECIMAL, value); self } #[inline] pub fn chars_uppercase(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CHARS_UPPERCASE, value); self } #[inline] pub fn chars_noblank(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CHARS_NO_BLANK, value); self } #[inline] pub fn auto_select_all(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::AUTO_SELECT_ALL, value); self } #[inline] pub fn enter_returns_true(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::ENTER_RETURNS_TRUE, value); self } #[inline] pub fn callback_completion(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CALLBACK_COMPLETION, value); self } #[inline] pub fn callback_history(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CALLBACK_HISTORY, value); self } #[inline] pub fn callback_always(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CALLBACK_ALWAYS, value); self } #[inline] pub fn callback_char_filter(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CALLBACK_CHAR_FILTER, value); self } #[inline] pub fn resize_buffer(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::CALLBACK_RESIZE, value); self } #[inline] pub fn allow_tab_input(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::ALLOW_TAB_INPUT, value); self } #[inline] pub fn no_horizontal_scroll(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::NO_HORIZONTAL_SCROLL, value); self } #[inline] pub fn always_insert_mode(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::ALWAYS_INSERT_MODE, value); self } #[inline] pub fn read_only(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::READ_ONLY, value); self } #[inline] pub fn password(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::PASSWORD, value); self } #[inline] pub fn no_undo_redo(mut self, value: bool) -> Self { self.flags.set(InputTextFlags::NO_UNDO_REDO, value); self } }; } macro_rules! impl_step_params { ($InputType:ident, $Value:ty) => { #[inline] pub fn step(mut self, value: $Value) -> Self { self.step = value; self } #[inline] pub fn step_fast(mut self, value: $Value) -> Self { self.step_fast = value; self } }; } extern "C" fn resize_callback(data: *mut sys::ImGuiInputTextCallbackData) -> c_int { unsafe { if (*data).EventFlag == InputTextFlags::CALLBACK_RESIZE.bits() as i32 { if let Some(buffer) = ((*data).UserData as *mut ImString).as_mut() { let requested_size = (*data).BufSize as usize; if requested_size > buffer.capacity_with_nul() { // Refresh the buffer's length to take into account changes made by dear imgui. buffer.refresh_len(); buffer.reserve(requested_size - buffer.0.len()); debug_assert!(buffer.capacity_with_nul() >= requested_size); (*data).Buf = buffer.as_mut_ptr(); (*data).BufDirty = true; } } } 0 } } #[must_use] pub struct InputText<'ui, 'p> { label: &'p ImStr, hint: Option<&'p ImStr>, buf: &'p mut ImString, flags: InputTextFlags, _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> InputText<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString) -> Self { InputText { label, hint: None, buf, flags: InputTextFlags::empty(), _phantom: PhantomData, } } /// Sets the hint displayed in the input text background. #[inline] pub fn hint(mut self, hint: &'p ImStr) -> Self { self.hint = Some(hint); self } impl_text_flags!(InputText); // TODO: boxed closure...? // pub fn callback(self) -> Self { } pub fn build(self) -> bool { let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); let (callback, data): (sys::ImGuiInputTextCallback, _) = { if self.flags.contains(InputTextFlags::CALLBACK_RESIZE) { (Some(resize_callback), self.buf as *mut _ as *mut c_void) } else { (None, ptr::null_mut()) } }; unsafe { let result = if let Some(hint) = self.hint { sys::igInputTextWithHint( self.label.as_ptr(), hint.as_ptr(), ptr, capacity, self.flags.bits() as i32, callback, data, ) } else { sys::igInputText( self.label.as_ptr(), ptr, capacity, self.flags.bits() as i32, callback, data, ) }; self.buf.refresh_len(); result } } } #[must_use] pub struct InputTextMultiline<'ui, 'p> { label: &'p ImStr, buf: &'p mut ImString, flags: InputTextFlags, size: [f32; 2], _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> InputTextMultiline<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString, size: [f32; 2]) -> Self { InputTextMultiline { label, buf, flags: InputTextFlags::empty(), size, _phantom: PhantomData, } } impl_text_flags!(InputText); // TODO: boxed closure...? // pub fn callback(self) -> Self { } pub fn build(self) -> bool { let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); let (callback, data): (sys::ImGuiInputTextCallback, _) = { if self.flags.contains(InputTextFlags::CALLBACK_RESIZE) { (Some(resize_callback), self.buf as *mut _ as *mut c_void) } else { (None, ptr::null_mut()) } }; unsafe { let result = sys::igInputTextMultiline( self.label.as_ptr(), ptr, capacity, self.size.into(), self.flags.bits() as i32, callback, data, ); self.buf.refresh_len(); result } } } #[must_use] pub struct InputInt<'ui, 'p> { label: &'p ImStr, value: &'p mut i32, step: i32, step_fast: i32, flags: InputTextFlags, _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> InputInt<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut i32) -> Self { InputInt { label, value, step: 1, step_fast: 100, flags: InputTextFlags::empty(), _phantom: PhantomData, } } pub fn build(self) -> bool { unsafe { sys::igInputInt( self.label.as_ptr(), self.value as *mut i32, self.step, self.step_fast, self.flags.bits() as i32, ) } } impl_step_params!(InputInt, i32); impl_text_flags!(InputInt); } #[must_use] pub struct InputFloat<'ui, 'p> { label: &'p ImStr, value: &'p mut f32, step: f32, step_fast: f32, flags: InputTextFlags, _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> InputFloat<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut f32) -> Self { InputFloat { label, value, step: 0.0, step_fast: 0.0, flags: InputTextFlags::empty(), _phantom: PhantomData, } } pub fn build(self) -> bool { unsafe { sys::igInputFloat( self.label.as_ptr(), self.value as *mut f32, self.step, self.step_fast, b"%.3f\0".as_ptr() as *const _, self.flags.bits() as i32, ) } } impl_step_params!(InputFloat, f32); impl_text_flags!(InputFloat); } macro_rules! impl_input_floatn { ($InputFloatN:ident, $N:expr, $igInputFloatN:ident) => { #[must_use] pub struct $InputFloatN<'ui, 'p> { label: &'p ImStr, value: &'p mut [f32; $N], flags: InputTextFlags, _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> $InputFloatN<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [f32; $N]) -> Self { $InputFloatN { label, value, flags: InputTextFlags::empty(), _phantom: PhantomData, } } pub fn build(self) -> bool { unsafe { sys::$igInputFloatN( self.label.as_ptr(), self.value.as_mut_ptr(), b"%.3f\0".as_ptr() as *const _, self.flags.bits() as i32, ) } } impl_text_flags!($InputFloatN); } }; } impl_input_floatn!(InputFloat2, 2, igInputFloat2); impl_input_floatn!(InputFloat3, 3, igInputFloat3); impl_input_floatn!(InputFloat4, 4, igInputFloat4); macro_rules! impl_input_intn { ($InputIntN:ident, $N:expr, $igInputIntN:ident) => { #[must_use] pub struct $InputIntN<'ui, 'p> { label: &'p ImStr, value: &'p mut [i32; $N], flags: InputTextFlags, _phantom: PhantomData<&'ui Ui<'ui>>, } impl<'ui, 'p> $InputIntN<'ui, 'p> { pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [i32; $N]) -> Self { $InputIntN { label, value, flags: InputTextFlags::empty(), _phantom: PhantomData, } } pub fn build(self) -> bool { unsafe { sys::$igInputIntN( self.label.as_ptr(), self.value.as_mut_ptr(), self.flags.bits() as i32, ) } } impl_text_flags!($InputIntN); } }; } impl_input_intn!(InputInt2, 2, igInputInt2); impl_input_intn!(InputInt3, 3, igInputInt3); impl_input_intn!(InputInt4, 4, igInputInt4);