redid buffer creation

This commit is contained in:
Jack Spira 2021-09-07 14:57:24 -07:00
parent 64f5787940
commit f011d9e337

View File

@ -118,18 +118,58 @@ pub enum EventDirection {
} }
pub struct TextCallbackBuffer<'a> { pub struct TextCallbackBuffer<'a> {
buf: &'a mut str, str: &'a mut str,
dirty: &'a mut bool, dirty: &'a mut bool,
cursor_pos: &'a mut i32, cursor_pos: &'a mut i32,
selection_start: &'a mut i32, selection_start: &'a mut i32,
selection_end: &'a mut i32, selection_end: &'a mut i32,
callback_data: *mut sys::ImGuiInputTextCallbackData, callback_data: *mut sys::ImGuiInputTextCallbackData,
buf_start: *const c_char,
buf_len: *const c_int,
} }
impl TextCallbackBuffer<'_> { impl<'a> TextCallbackBuffer<'a> {
/// Creates the buffer.
unsafe fn new(data: &'a mut sys::ImGuiInputTextCallbackData) -> Self {
let ptr = data as *mut _;
let mut output = Self {
// specifically, this will bork in resize
str: std::str::from_utf8_mut(&mut []).unwrap(),
dirty: &mut data.BufDirty,
cursor_pos: &mut data.CursorPos,
selection_start: &mut data.SelectionStart,
selection_end: &mut data.SelectionEnd,
callback_data: ptr,
buf_start: (*data).Buf as *const _,
buf_len: &(*data).BufTextLen as *const i32,
};
output.refresh_str();
output
}
/// Refreshes the public facing `str`, such that it appears
/// to users as if we're working with a normal Rust string.
///
/// You should only need to call this if you use [str_as_bytes_mut].
pub fn refresh_str(&mut self) {
// safety -- we never hand out mutable pointers to BufStart or BufLen,
// so this is always set by imgui, and imgui always is utf8,
// but we are more or less trusting that imgui won't boof this.
unsafe {
self.str = std::str::from_utf8_mut(std::slice::from_raw_parts_mut(
self.buf_start as *const _ as *mut _,
*self.buf_len as usize,
))
.expect("internal imgui error -- it boofed a utf8")
}
}
/// Get a reference to the text callback buffer's buf. /// Get a reference to the text callback buffer's buf.
pub fn buf(&self) -> &str { pub fn str(&self) -> &str {
self.buf self.str
} }
/// Gives access to the underlying byte array MUTABLY. /// Gives access to the underlying byte array MUTABLY.
@ -140,14 +180,15 @@ impl TextCallbackBuffer<'_> {
/// upheld: /// upheld:
/// 1. Keep the data utf8 valid. /// 1. Keep the data utf8 valid.
/// 2. After editing the string, call [set_dirty]. /// 2. After editing the string, call [set_dirty].
/// 3. Finally, call [refresh_str].
/// ///
/// We present this string with a `str` interface immutably, which /// We present this string with a `str` interface immutably, which
/// actually somewhat weakens `trunc` operations on the string. /// actually somewhat weakens `trunc` operations on the string.
/// You can use [remove_chars] to handle this operation completely /// You can use [remove_chars] to handle this operation completely
/// safely for you. However, if this is still too limiting, /// safely for you. However, if this is still too limiting,
/// please submit an issue. /// please submit an issue.
pub unsafe fn buf_mut(&mut self) -> &mut [u8] { pub unsafe fn str_as_bytes_mut(&mut self) -> &mut [u8] {
self.buf.as_bytes_mut() self.str.as_bytes_mut()
} }
/// Sets the dirty flag on the text to imgui, indicating that /// Sets the dirty flag on the text to imgui, indicating that
@ -173,7 +214,7 @@ impl TextCallbackBuffer<'_> {
/// Returns the selected text directly. Note that if no text is selected, /// Returns the selected text directly. Note that if no text is selected,
/// an empty str slice will be returned. /// an empty str slice will be returned.
pub fn selected(&self) -> &str { pub fn selected(&self) -> &str {
&self.buf[self.selection()] &self.str[self.selection()]
} }
/// Sets the cursor to select all. This is always a valid operation, /// Sets the cursor to select all. This is always a valid operation,
@ -197,6 +238,16 @@ impl TextCallbackBuffer<'_> {
unsafe { sys::ImGuiInputTextCallbackData_HasSelection(self.callback_data) } unsafe { sys::ImGuiInputTextCallbackData_HasSelection(self.callback_data) }
} }
/// Pushes the given str to the end of this buffer. If this
/// would require the String to resize, it will be resized by calling the
/// `CALLBACK_RESIZE` callback. This is automatically handled.
pub fn push_str(&mut self, s: &str) {
// this is safe because the ench of a self.str is a char_boundary.
unsafe {
self.insert_chars_unsafe(self.str.len(), s);
}
}
/// Inserts the given string at the given position. If this /// Inserts the given string at the given position. If this
/// would require the String to resize, it will be resized by calling the /// would require the String to resize, it will be resized by calling the
/// `CALLBACK_RESIZE` callback. This is automatically handled. /// `CALLBACK_RESIZE` callback. This is automatically handled.
@ -204,7 +255,7 @@ impl TextCallbackBuffer<'_> {
/// ## Panics /// ## Panics
/// Panics if the `pos` is not a char_boundary. /// Panics if the `pos` is not a char_boundary.
pub fn insert_chars(&mut self, pos: usize, s: &str) { pub fn insert_chars(&mut self, pos: usize, s: &str) {
assert!(self.buf.is_char_boundary(pos)); assert!(self.str.is_char_boundary(pos));
unsafe { unsafe {
self.insert_chars_unsafe(pos, s); self.insert_chars_unsafe(pos, s);
} }
@ -227,13 +278,14 @@ impl TextCallbackBuffer<'_> {
pos as i32, pos as i32,
start as *const c_char, start as *const c_char,
end as *const c_char, end as *const c_char,
) );
self.refresh_str();
} }
/// Clears the string. /// Clears the string to an empty buffer.
pub fn clear(&mut self) { pub fn clear(&mut self) {
unsafe { unsafe {
self.remove_chars_unchecked(0, self.buf().len()); self.remove_chars_unchecked(0, self.str.len());
} }
} }
@ -244,7 +296,7 @@ impl TextCallbackBuffer<'_> {
/// Panics if the `pos` is not a char boundary or if /// Panics if the `pos` is not a char boundary or if
/// there are not enough chars remaining. /// there are not enough chars remaining.
pub fn remove_chars(&mut self, pos: usize, char_count: usize) { pub fn remove_chars(&mut self, pos: usize, char_count: usize) {
let inner = &self.buf[pos..]; let inner = &self.str[pos..];
let byte_count = inner let byte_count = inner
.char_indices() .char_indices()
.nth(char_count) .nth(char_count)
@ -269,7 +321,8 @@ impl TextCallbackBuffer<'_> {
self.callback_data, self.callback_data,
pos as i32, pos as i32,
byte_count as i32, byte_count as i32,
) );
self.refresh_str();
} }
/// Get a reference to the text callback buffer's cursor pos. /// Get a reference to the text callback buffer's cursor pos.
@ -316,22 +369,7 @@ extern "C" fn callback(data: *mut sys::ImGuiInputTextCallbackData) -> c_int {
let make_txt_data = || { let make_txt_data = || {
// This safe in every callback EXCEPT RESIZE. // This safe in every callback EXCEPT RESIZE.
unsafe { unsafe { TextCallbackBuffer::new(&mut *data) }
TextCallbackBuffer {
// specifically, this will bork in resize
buf: std::str::from_utf8_mut(std::slice::from_raw_parts_mut(
(*data).Buf as *mut u8,
(*data).BufTextLen as usize,
))
.expect("internal imgui error -- it boofed a utf8"),
dirty: &mut (*data).BufDirty,
cursor_pos: &mut (*data).CursorPos,
selection_start: &mut (*data).SelectionStart,
selection_end: &mut (*data).SelectionEnd,
callback_data: data,
}
}
}; };
// check this callback. // check this callback.