diff --git a/src/input.rs b/src/input.rs index e59d790..97b0a24 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::os::raw::{c_int, c_void}; use std::ptr; use sys; @@ -72,6 +73,12 @@ macro_rules! impl_text_flags { self } + #[inline] + pub fn resize_buffer(mut self, value: bool) -> Self { + self.flags.set(ImGuiInputTextFlags::CallbackResize, value); + self + } + #[inline] pub fn allow_tab_input(mut self, value: bool) -> Self { self.flags.set(ImGuiInputTextFlags::AllowTabInput, value); @@ -126,6 +133,27 @@ macro_rules! impl_step_params { } } +extern "C" fn resize_callback(data: *mut sys::ImGuiInputTextCallbackData) -> c_int { + unsafe { + if (*data).event_flag == ImGuiInputTextFlags::CallbackResize { + if let Some(buffer) = ((*data).user_data as *mut ImString).as_mut() { + let requested_size = (*data).buf_size 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(); + // Add 1 to include the null terminator, so that reserve sees the right length. + // After we're done we'll call refresh_len, so this won't be visible to the user. + buffer.0.set_len(buffer.0.len() + 1); + buffer.reserve(requested_size - buffer.0.len()); + (*data).buf = buffer.as_mut_ptr(); + (*data).buf_dirty = true; + } + } + } + return 0 + } +} + #[must_use] pub struct InputText<'ui, 'p> { label: &'p ImStr, @@ -150,15 +178,19 @@ impl<'ui, 'p> InputText<'ui, 'p> { // 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(ImGuiInputTextFlags::CallbackResize) { + (Some(resize_callback), self.buf as *mut _ as *mut c_void) + } else { + (None, ptr::null_mut()) + } + }; + unsafe { - sys::igInputText( - self.label.as_ptr(), - self.buf.as_mut_ptr(), - self.buf.capacity_with_nul(), - self.flags, - None, - ptr::null_mut(), - ) + let result = sys::igInputText(self.label.as_ptr(), ptr, capacity, self.flags, callback, data); + self.buf.refresh_len(); + result } } } @@ -189,16 +221,19 @@ impl<'ui, 'p> InputTextMultiline<'ui, 'p> { // 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(ImGuiInputTextFlags::CallbackResize) { + (Some(resize_callback), self.buf as *mut _ as *mut c_void) + } else { + (None, ptr::null_mut()) + } + }; + unsafe { - sys::igInputTextMultiline( - self.label.as_ptr(), - self.buf.as_mut_ptr(), - self.buf.capacity_with_nul(), - self.size, - self.flags, - None, - ptr::null_mut(), - ) + let result = sys::igInputTextMultiline(self.label.as_ptr(), ptr, capacity, self.size, self.flags, callback, data); + self.buf.refresh_len(); + result } } } diff --git a/src/string.rs b/src/string.rs index 58f87a8..ecf77e8 100644 --- a/src/string.rs +++ b/src/string.rs @@ -6,7 +6,7 @@ use std::os::raw::c_char; use std::str; #[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)] -pub struct ImString(Vec); +pub struct ImString(pub(crate) Vec); impl ImString { pub fn new>(value: T) -> ImString { @@ -61,7 +61,7 @@ impl ImString { /// Dear imgui accesses pointers directly, so the length doesn't get updated when the contents /// change. This is normally OK, because Deref to ImStr always calculates the slice length /// based on contents. However, we need to refresh the length in some ImString functions. - fn refresh_len(&mut self) { + pub(crate) fn refresh_len(&mut self) { let len = self.to_str().len(); unsafe { self.0.set_len(len);