diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 8e2fa8b..0510ce6 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -25,6 +25,15 @@ - `ImColor` (which is a wrapper around `u32`) has been renamed to `ImColor32` in order to avoid confusion with the `ImColor` type from the Dear ImGui C++ code (which is a wrapper around `ImVec4`). In the future an `ImColor` type which maps more closely to the C++ one will be added. - Additionally, a number of constructor and accessor methods have been added to it `ImColor`, which are `const fn` where possible. +- The `im_str!` macro can now be used in `const` contexts (when the `format!` version is not used). + +- `im_str!` now verifies that the parameter has no interior nuls at compile time. This can be avoided to get the old (truncating) behavior by forcing it to use the `format!`-like version, e.g. `im_str!("for_some_reason_this_should_be_truncated\0 there {}", "")`. + - This is not recommended, and is probably not useful. + +- Many functions are now `const fn`. + +- A large number of small functions are now `#[inline]`, but many still aren't, so you probably will want to build with LTO for release builds if you use `imgui` heavily. + ## [0.6.1] - 2020-12-16 - Support for winit 0.24.x diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index dd86c53..cdfa511 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -13,11 +13,11 @@ pub use crate::bindings::*; impl ImVec2 { #[inline] - pub fn new(x: f32, y: f32) -> ImVec2 { + pub const fn new(x: f32, y: f32) -> ImVec2 { ImVec2 { x, y } } #[inline] - pub fn zero() -> ImVec2 { + pub const fn zero() -> ImVec2 { ImVec2 { x: 0.0, y: 0.0 } } } @@ -36,27 +36,27 @@ impl From<(f32, f32)> for ImVec2 { } } -impl Into<[f32; 2]> for ImVec2 { +impl From for [f32; 2] { #[inline] - fn into(self) -> [f32; 2] { - [self.x, self.y] + fn from(v: ImVec2) -> [f32; 2] { + [v.x, v.y] } } -impl Into<(f32, f32)> for ImVec2 { +impl From for (f32, f32) { #[inline] - fn into(self) -> (f32, f32) { - (self.x, self.y) + fn from(v: ImVec2) -> (f32, f32) { + (v.x, v.y) } } impl ImVec4 { #[inline] - pub fn new(x: f32, y: f32, z: f32, w: f32) -> ImVec4 { + pub const fn new(x: f32, y: f32, z: f32, w: f32) -> ImVec4 { ImVec4 { x, y, z, w } } #[inline] - pub fn zero() -> ImVec4 { + pub const fn zero() -> ImVec4 { ImVec4 { x: 0.0, y: 0.0, @@ -80,17 +80,17 @@ impl From<(f32, f32, f32, f32)> for ImVec4 { } } -impl Into<[f32; 4]> for ImVec4 { +impl From for [f32; 4] { #[inline] - fn into(self) -> [f32; 4] { - [self.x, self.y, self.z, self.w] + fn from(v: ImVec4) -> [f32; 4] { + [v.x, v.y, v.z, v.w] } } -impl Into<(f32, f32, f32, f32)> for ImVec4 { +impl From for (f32, f32, f32, f32) { #[inline] - fn into(self) -> (f32, f32, f32, f32) { - (self.x, self.y, self.z, self.w) + fn from(v: ImVec4) -> (f32, f32, f32, f32) { + (v.x, v.y, v.z, v.w) } } diff --git a/imgui/src/clipboard.rs b/imgui/src/clipboard.rs index 369a3fa..e6ff182 100644 --- a/imgui/src/clipboard.rs +++ b/imgui/src/clipboard.rs @@ -23,6 +23,7 @@ pub(crate) struct ClipboardContext { } impl ClipboardContext { + #[inline] pub fn new(backend: Box) -> ClipboardContext { ClipboardContext { backend, diff --git a/imgui/src/input/keyboard.rs b/imgui/src/input/keyboard.rs index e851db9..a2a586b 100644 --- a/imgui/src/input/keyboard.rs +++ b/imgui/src/input/keyboard.rs @@ -80,6 +80,7 @@ pub enum FocusedWidget { } impl FocusedWidget { + #[inline] fn as_offset(self) -> i32 { match self { FocusedWidget::Previous => -1, @@ -94,12 +95,14 @@ impl<'ui> Ui<'ui> { /// Returns the key index of the given key identifier. /// /// Equivalent to indexing the Io struct `key_map` field: `ui.io().key_map[key]` + #[inline] fn key_index(&self, key: Key) -> i32 { unsafe { sys::igGetKeyIndex(key as i32) } } /// Returns true if the key is being held. /// /// Equivalent to indexing the Io struct `keys_down` field: `ui.io().keys_down[key_index]` + #[inline] pub fn is_key_down(&self, key: Key) -> bool { let key_index = self.key_index(key); unsafe { sys::igIsKeyDown(key_index) } @@ -107,11 +110,13 @@ impl<'ui> Ui<'ui> { /// Returns true if the key was pressed (went from !down to down). /// /// Affected by key repeat settings (`io.key_repeat_delay`, `io.key_repeat_rate`) + #[inline] pub fn is_key_pressed(&self, key: Key) -> bool { let key_index = self.key_index(key); unsafe { sys::igIsKeyPressed(key_index, true) } } /// Returns true if the key was released (went from down to !down) + #[inline] pub fn is_key_released(&self, key: Key) -> bool { let key_index = self.key_index(key); unsafe { sys::igIsKeyReleased(key_index) } @@ -120,11 +125,13 @@ impl<'ui> Ui<'ui> { /// /// Usually returns 0 or 1, but might be >1 if `rate` is small enough that `io.delta_time` > /// `rate`. + #[inline] pub fn key_pressed_amount(&self, key: Key, repeat_delay: f32, rate: f32) -> u32 { let key_index = self.key_index(key); unsafe { sys::igGetKeyPressedAmount(key_index, repeat_delay, rate) as u32 } } /// Focuses keyboard on a widget relative to current position + #[inline] pub fn set_keyboard_focus_here(&self, target_widget: FocusedWidget) { unsafe { sys::igSetKeyboardFocusHere(target_widget.as_offset()); diff --git a/imgui/src/internal.rs b/imgui/src/internal.rs index cdd86dc..d3d10d4 100644 --- a/imgui/src/internal.rs +++ b/imgui/src/internal.rs @@ -12,6 +12,7 @@ pub struct ImVector { } impl ImVector { + #[inline] pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.data, self.size as usize) } } @@ -71,6 +72,7 @@ pub unsafe trait RawCast: Sized { /// # Safety /// /// It is up to the caller to guarantee the cast is valid. + #[inline] unsafe fn from_raw(raw: &T) -> &Self { &*(raw as *const _ as *const Self) } @@ -79,6 +81,7 @@ pub unsafe trait RawCast: Sized { /// # Safety /// /// It is up to the caller to guarantee the cast is valid. + #[inline] unsafe fn from_raw_mut(raw: &mut T) -> &mut Self { &mut *(raw as *mut _ as *mut Self) } @@ -87,6 +90,7 @@ pub unsafe trait RawCast: Sized { /// # Safety /// /// It is up to the caller to guarantee the cast is valid. + #[inline] unsafe fn raw(&self) -> &T { &*(self as *const _ as *const T) } @@ -95,6 +99,7 @@ pub unsafe trait RawCast: Sized { /// # Safety /// /// It is up to the caller to guarantee the cast is valid. + #[inline] unsafe fn raw_mut(&mut self) -> &mut T { &mut *(self as *mut _ as *mut T) } @@ -182,27 +187,33 @@ pub trait InclusiveRangeBounds { } impl InclusiveRangeBounds for RangeFrom { + #[inline] fn start_bound(&self) -> Option<&T> { Some(&self.start) } + #[inline] fn end_bound(&self) -> Option<&T> { None } } impl InclusiveRangeBounds for RangeInclusive { + #[inline] fn start_bound(&self) -> Option<&T> { Some(self.start()) } + #[inline] fn end_bound(&self) -> Option<&T> { Some(self.end()) } } impl InclusiveRangeBounds for RangeToInclusive { + #[inline] fn start_bound(&self) -> Option<&T> { None } + #[inline] fn end_bound(&self) -> Option<&T> { Some(&self.end) } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 5b1064d..1db2500 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -50,6 +50,9 @@ pub use self::window::child_window::*; pub use self::window::*; use internal::RawCast; +#[macro_use] +mod string; + mod clipboard; pub mod color; mod columns; @@ -68,7 +71,6 @@ mod plotlines; mod popup_modal; mod render; mod stacks; -mod string; mod style; #[cfg(test)] mod test; @@ -76,6 +78,11 @@ mod utils; mod widget; mod window; +// Used by macros. Underscores are just to make it clear it's not part of the +// public API. +#[doc(hidden)] +pub use core as __core; + /// Returns the underlying Dear ImGui library version pub fn dear_imgui_version() -> &'static str { unsafe { @@ -199,24 +206,28 @@ pub enum Id<'a> { } impl From for Id<'static> { + #[inline] fn from(i: i32) -> Self { Id::Int(i) } } impl<'a, T: ?Sized + AsRef> From<&'a T> for Id<'a> { + #[inline] fn from(s: &'a T) -> Self { Id::Str(s.as_ref()) } } impl From<*const T> for Id<'static> { + #[inline] fn from(p: *const T) -> Self { Id::Ptr(p as *const c_void) } } impl From<*mut T> for Id<'static> { + #[inline] fn from(p: *mut T) -> Self { Id::Ptr(p as *const T as *const c_void) } diff --git a/imgui/src/list_clipper.rs b/imgui/src/list_clipper.rs index a595d26..db2bfa3 100644 --- a/imgui/src/list_clipper.rs +++ b/imgui/src/list_clipper.rs @@ -10,14 +10,14 @@ pub struct ListClipper { } impl ListClipper { - pub fn new(items_count: i32) -> Self { + pub const fn new(items_count: i32) -> Self { ListClipper { items_count, items_height: -1.0, } } - pub fn items_height(mut self, items_height: f32) -> Self { + pub const fn items_height(mut self, items_height: f32) -> Self { self.items_height = items_height; self } diff --git a/imgui/src/plothistogram.rs b/imgui/src/plothistogram.rs index fe4eec2..e58c472 100644 --- a/imgui/src/plothistogram.rs +++ b/imgui/src/plothistogram.rs @@ -17,7 +17,7 @@ pub struct PlotHistogram<'ui, 'p> { } impl<'ui, 'p> PlotHistogram<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { + pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { PlotHistogram { label, values, @@ -31,31 +31,31 @@ impl<'ui, 'p> PlotHistogram<'ui, 'p> { } #[inline] - pub fn values_offset(mut self, values_offset: usize) -> Self { + pub const fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } #[inline] - pub fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { + pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { self.overlay_text = Some(overlay_text); self } #[inline] - pub fn scale_min(mut self, scale_min: f32) -> Self { + pub const fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } #[inline] - pub fn scale_max(mut self, scale_max: f32) -> Self { + pub const fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } #[inline] - pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } diff --git a/imgui/src/plotlines.rs b/imgui/src/plotlines.rs index b329918..9f7f4c1 100644 --- a/imgui/src/plotlines.rs +++ b/imgui/src/plotlines.rs @@ -17,7 +17,7 @@ pub struct PlotLines<'ui, 'p> { } impl<'ui, 'p> PlotLines<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { + pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { PlotLines { label, values, @@ -31,31 +31,31 @@ impl<'ui, 'p> PlotLines<'ui, 'p> { } #[inline] - pub fn values_offset(mut self, values_offset: usize) -> Self { + pub const fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } #[inline] - pub fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { + pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { self.overlay_text = Some(overlay_text); self } #[inline] - pub fn scale_min(mut self, scale_min: f32) -> Self { + pub const fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } #[inline] - pub fn scale_max(mut self, scale_max: f32) -> Self { + pub const fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } #[inline] - pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } diff --git a/imgui/src/render/draw_data.rs b/imgui/src/render/draw_data.rs index 4272eba..1627e85 100644 --- a/imgui/src/render/draw_data.rs +++ b/imgui/src/render/draw_data.rs @@ -36,6 +36,7 @@ unsafe impl RawCast for DrawData {} impl DrawData { /// Returns an iterator over the draw lists included in the draw data. + #[inline] pub fn draw_lists(&self) -> DrawListIterator { unsafe { DrawListIterator { @@ -44,10 +45,12 @@ impl DrawData { } } /// Returns the number of draw lists included in the draw data. + #[inline] pub fn draw_lists_count(&self) -> usize { use std::convert::TryInto; self.cmd_lists_count.try_into().unwrap() } + #[inline] pub(crate) unsafe fn cmd_lists(&self) -> &[*const DrawList] { slice::from_raw_parts( self.cmd_lists as *const *const DrawList, @@ -124,21 +127,25 @@ pub struct DrawList(sys::ImDrawList); impl RawWrapper for DrawList { type Raw = sys::ImDrawList; + #[inline] unsafe fn raw(&self) -> &sys::ImDrawList { &self.0 } + #[inline] unsafe fn raw_mut(&mut self) -> &mut sys::ImDrawList { &mut self.0 } } impl DrawList { + #[inline] pub(crate) unsafe fn cmd_buffer(&self) -> &[sys::ImDrawCmd] { slice::from_raw_parts( self.0.CmdBuffer.Data as *const sys::ImDrawCmd, self.0.CmdBuffer.Size as usize, ) } + #[inline] pub fn idx_buffer(&self) -> &[DrawIdx] { unsafe { slice::from_raw_parts( @@ -147,6 +154,7 @@ impl DrawList { ) } } + #[inline] pub fn vtx_buffer(&self) -> &[DrawVert] { unsafe { slice::from_raw_parts( @@ -170,6 +178,7 @@ impl DrawList { slice::from_raw_parts(self.0.VtxBuffer.Data.cast(), self.0.VtxBuffer.Size as usize) } + #[inline] pub fn commands(&self) -> DrawCmdIterator { unsafe { DrawCmdIterator { @@ -186,6 +195,7 @@ pub struct DrawCmdIterator<'a> { impl<'a> Iterator for DrawCmdIterator<'a> { type Item = DrawCmd; + #[inline] fn next(&mut self) -> Option { self.iter.next().map(|cmd| { let cmd_params = DrawCmdParams { diff --git a/imgui/src/render/renderer.rs b/imgui/src/render/renderer.rs index ec515b0..2b87e99 100644 --- a/imgui/src/render/renderer.rs +++ b/imgui/src/render/renderer.rs @@ -7,29 +7,34 @@ pub struct TextureId(usize); impl TextureId { /// Creates a new texture id with the given identifier. - pub fn new(id: usize) -> Self { + #[inline] + pub const fn new(id: usize) -> Self { Self(id) } /// Returns the id of the TextureId. - pub fn id(self) -> usize { + #[inline] + pub const fn id(self) -> usize { self.0 } } impl From for TextureId { + #[inline] fn from(id: usize) -> Self { TextureId(id) } } impl From<*const T> for TextureId { + #[inline] fn from(ptr: *const T) -> Self { TextureId(ptr as usize) } } impl From<*mut T> for TextureId { + #[inline] fn from(ptr: *mut T) -> Self { TextureId(ptr as usize) } @@ -56,6 +61,8 @@ pub struct Textures { } impl Textures { + // TODO: hasher like rustc_hash::FxHashMap or something would let this be + // `const fn` pub fn new() -> Self { Textures { textures: HashMap::new(), diff --git a/imgui/src/string.rs b/imgui/src/string.rs index 6a3f58a..9b7d91a 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -7,19 +7,34 @@ use std::str; #[macro_export] macro_rules! im_str { - ($e:tt) => ({ - unsafe { - $crate::ImStr::from_utf8_with_nul_unchecked(concat!($e, "\0").as_bytes()) + ($e:literal $(,)?) => {{ + const __INPUT: &str = concat!($e, "\0"); + { + // Trigger a compile error if there's an interior NUL character. + const _CHECK_NUL: [(); 0] = [(); { + let bytes = __INPUT.as_bytes(); + let mut i = 0; + let mut found_nul = 0; + while i < bytes.len() - 1 && found_nul == 0 { + if bytes[i] == 0 { + found_nul = 1; + } + i += 1; + } + found_nul + }]; + const RESULT: &'static $crate::ImStr = unsafe { + $crate::__core::mem::transmute::<&'static [u8], &'static $crate::ImStr>(__INPUT.as_bytes()) + }; + RESULT } + }}; + ($e:literal, $($arg:tt)+) => ({ + $crate::ImString::new(format!($e, $($arg)*)) }); - ($e:tt, $($arg:tt)*) => ({ - unsafe { - $crate::ImString::from_utf8_with_nul_unchecked(format!(concat!($e, "\0"), $($arg)*).into_bytes()) - } - }) } -/// A UTF-8 encoded, growable, implicitly null-terminated string. +/// A UTF-8 encoded, growable, implicitly nul-terminated string. #[derive(Clone, Hash, Ord, Eq, PartialOrd, PartialEq)] pub struct ImString(pub(crate) Vec); @@ -32,42 +47,54 @@ impl ImString { s } } + /// Creates a new empty `ImString` with a particular capacity + #[inline] pub fn with_capacity(capacity: usize) -> ImString { let mut v = Vec::with_capacity(capacity + 1); v.push(b'\0'); ImString(v) } + /// Converts a vector of bytes to a `ImString` without checking that the string contains valid /// UTF-8 /// /// # Safety /// /// It is up to the caller to guarantee the vector contains valid UTF-8 and no null terminator. + #[inline] pub unsafe fn from_utf8_unchecked(mut v: Vec) -> ImString { v.push(b'\0'); ImString(v) } + /// Converts a vector of bytes to a `ImString` without checking that the string contains valid /// UTF-8 /// /// # Safety /// /// It is up to the caller to guarantee the vector contains valid UTF-8 and a null terminator. + #[inline] pub unsafe fn from_utf8_with_nul_unchecked(v: Vec) -> ImString { ImString(v) } + /// Truncates this `ImString`, removing all contents + #[inline] pub fn clear(&mut self) { self.0.clear(); self.0.push(b'\0'); } + /// Appends the given character to the end of this `ImString` + #[inline] pub fn push(&mut self, ch: char) { let mut buf = [0; 4]; self.push_str(ch.encode_utf8(&mut buf)); } + /// Appends a given string slice to the end of this `ImString` + #[inline] pub fn push_str(&mut self, string: &str) { self.0.pop(); self.0.extend(string.bytes()); @@ -76,14 +103,19 @@ impl ImString { self.refresh_len(); } } + /// Returns the capacity of this `ImString` in bytes + #[inline] pub fn capacity(&self) -> usize { self.0.capacity() - 1 } + /// Returns the capacity of this `ImString` in bytes, including the implicit null byte + #[inline] pub fn capacity_with_nul(&self) -> usize { self.0.capacity() } + /// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the /// current length. /// @@ -91,21 +123,27 @@ impl ImString { pub fn reserve(&mut self, additional: usize) { self.0.reserve(additional); } + /// Ensures that the capacity of this `ImString` is at least `additional` bytes larger than the /// current length pub fn reserve_exact(&mut self, additional: usize) { self.0.reserve_exact(additional); } + /// Returns a raw pointer to the underlying buffer + #[inline] pub fn as_ptr(&self) -> *const c_char { self.0.as_ptr() as *const c_char } + /// Returns a raw mutable pointer to the underlying buffer. /// /// If the underlying data is modified, `refresh_len` *must* be called afterwards. + #[inline] pub fn as_mut_ptr(&mut self) -> *mut c_char { self.0.as_mut_ptr() as *mut c_char } + /// Updates the underlying buffer length based on the current contents. /// /// This function *must* be called if the underlying data is modified via a pointer @@ -115,6 +153,7 @@ impl ImString { /// /// It is up to the caller to guarantee the this ImString contains valid UTF-8 and a null /// terminator. + #[inline] pub unsafe fn refresh_len(&mut self) { let len = CStr::from_ptr(self.0.as_ptr() as *const c_char) .to_bytes_with_nul() @@ -124,54 +163,63 @@ impl ImString { } impl<'a> Default for ImString { + #[inline] fn default() -> ImString { ImString(vec![b'\0']) } } impl From for ImString { + #[inline] fn from(s: String) -> ImString { ImString::new(s) } } impl<'a> From for Cow<'a, ImStr> { + #[inline] fn from(s: ImString) -> Cow<'a, ImStr> { Cow::Owned(s) } } impl<'a> From<&'a ImString> for Cow<'a, ImStr> { + #[inline] fn from(s: &'a ImString) -> Cow<'a, ImStr> { Cow::Borrowed(s) } } impl<'a, T: ?Sized + AsRef> From<&'a T> for ImString { + #[inline] fn from(s: &'a T) -> ImString { s.as_ref().to_owned() } } impl AsRef for ImString { + #[inline] fn as_ref(&self) -> &ImStr { self } } impl Borrow for ImString { + #[inline] fn borrow(&self) -> &ImStr { self } } impl AsRef for ImString { + #[inline] fn as_ref(&self) -> &str { self.to_str() } } impl Borrow for ImString { + #[inline] fn borrow(&self) -> &str { self.to_str() } @@ -179,18 +227,21 @@ impl Borrow for ImString { impl Index for ImString { type Output = ImStr; + #[inline] fn index(&self, _index: RangeFull) -> &ImStr { self } } impl fmt::Debug for ImString { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.to_str(), f) } } impl fmt::Display for ImString { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.to_str(), f) } @@ -198,6 +249,7 @@ impl fmt::Display for ImString { impl Deref for ImString { type Target = ImStr; + #[inline] fn deref(&self) -> &ImStr { // as_ptr() is used, because we need to look at the bytes to figure out the length // self.0.len() is incorrect, because there might be more than one nul byte in the end, or @@ -222,11 +274,13 @@ impl fmt::Write for ImString { } } -/// A UTF-8 encoded, implicitly null-terminated string slice. +/// A UTF-8 encoded, implicitly nul-terminated string slice. #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct ImStr(CStr); +#[repr(transparent)] +pub struct ImStr([u8]); impl<'a> Default for &'a ImStr { + #[inline] fn default() -> &'a ImStr { static SLICE: &[u8] = &[0]; unsafe { ImStr::from_utf8_with_nul_unchecked(SLICE) } @@ -234,12 +288,14 @@ impl<'a> Default for &'a ImStr { } impl fmt::Debug for ImStr { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.0, f) } } impl fmt::Display for ImStr { + #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.to_str(), f) } @@ -252,60 +308,91 @@ impl ImStr { /// /// It is up to the caller to guarantee the pointer is not null and it points to a /// null-terminated UTF-8 string valid for the duration of the arbitrary lifetime 'a. + #[inline] pub unsafe fn from_ptr_unchecked<'a>(ptr: *const c_char) -> &'a ImStr { ImStr::from_cstr_unchecked(CStr::from_ptr(ptr)) } + /// Converts a slice of bytes to an imgui-rs string slice without checking for valid UTF-8 or /// null termination. /// /// # Safety /// /// It is up to the caller to guarantee the slice contains valid UTF-8 and a null terminator. + #[inline] pub unsafe fn from_utf8_with_nul_unchecked(bytes: &[u8]) -> &ImStr { &*(bytes as *const [u8] as *const ImStr) } + /// Converts a CStr reference to an imgui-rs string slice without checking for valid UTF-8. /// /// # Safety /// /// It is up to the caller to guarantee the CStr reference contains valid UTF-8. + #[inline] pub unsafe fn from_cstr_unchecked(value: &CStr) -> &ImStr { - &*(value as *const CStr as *const ImStr) + &*(value.to_bytes_with_nul() as *const [u8] as *const ImStr) } + /// Converts an imgui-rs string slice to a raw pointer + #[inline] pub fn as_ptr(&self) -> *const c_char { - self.0.as_ptr() + self.0.as_ptr() as *const c_char } + /// Converts an imgui-rs string slice to a normal string slice + #[inline] pub fn to_str(&self) -> &str { - // CStr::to_bytes does *not* include the null terminator - unsafe { str::from_utf8_unchecked(self.0.to_bytes()) } + self.sanity_check(); + unsafe { str::from_utf8_unchecked(&self.0[..(self.0.len() - 1)]) } } + /// Returns true if the imgui-rs string slice is empty + #[inline] pub fn is_empty(&self) -> bool { - self.0.to_bytes().is_empty() + debug_assert!(self.0.len() != 0); + self.0.len() == 1 + } + + // TODO: if this is too slow, avoid the UTF8 validation except if we'd + // already be doing O(n) stuff. + #[inline] + fn sanity_check(&self) { + debug_assert!( + str::from_utf8(&self.0).is_ok() + && !self.0.is_empty() + && !self.0[..(self.0.len() - 1)].contains(&0u8) + && self.0[self.0.len() - 1] == 0, + "bad ImStr: {:?}", + &self.0 + ); } } impl AsRef for ImStr { + #[inline] fn as_ref(&self) -> &CStr { - &self.0 + // Safety: our safety requirements are a superset of CStr's, so this is fine + unsafe { CStr::from_bytes_with_nul_unchecked(&self.0) } } } impl AsRef for ImStr { + #[inline] fn as_ref(&self) -> &ImStr { self } } impl AsRef for ImStr { + #[inline] fn as_ref(&self) -> &str { self.to_str() } } impl<'a> From<&'a ImStr> for Cow<'a, ImStr> { + #[inline] fn from(s: &'a ImStr) -> Cow<'a, ImStr> { Cow::Borrowed(s) } @@ -313,8 +400,10 @@ impl<'a> From<&'a ImStr> for Cow<'a, ImStr> { impl ToOwned for ImStr { type Owned = ImString; + #[inline] fn to_owned(&self) -> ImString { - ImString(self.0.to_owned().into_bytes()) + self.sanity_check(); + ImString(self.0.to_owned()) } } diff --git a/imgui/src/style.rs b/imgui/src/style.rs index cd952a1..f7f12c8 100644 --- a/imgui/src/style.rs +++ b/imgui/src/style.rs @@ -182,12 +182,14 @@ impl Style { impl Index for Style { type Output = [f32; 4]; + #[inline] fn index(&self, index: StyleColor) -> &[f32; 4] { &self.colors[index as usize] } } impl IndexMut for Style { + #[inline] fn index_mut(&mut self, index: StyleColor) -> &mut [f32; 4] { &mut self.colors[index as usize] } diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index 30906a9..6774c5b 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -66,25 +66,28 @@ pub struct ComboBox<'a> { impl<'a> ComboBox<'a> { /// Constructs a new combo box builder. - pub fn new(label: &'a ImStr) -> ComboBox<'a> { + pub const fn new(label: &'a ImStr) -> ComboBox<'a> { ComboBox { label, preview_value: None, flags: ComboBoxFlags::empty(), } } + /// Sets the preview value displayed in the preview box (if visible). #[inline] - pub fn preview_value(mut self, preview_value: &'a ImStr) -> Self { + pub const fn preview_value(mut self, preview_value: &'a ImStr) -> Self { self.preview_value = Some(preview_value); self } + /// Replaces all current settings with the given flags. #[inline] - pub fn flags(mut self, flags: ComboBoxFlags) -> Self { + pub const fn flags(mut self, flags: ComboBoxFlags) -> Self { self.flags = flags; self } + /// Enables/disables aligning the combo box popup toward the left. /// /// Disabled by default. @@ -94,6 +97,7 @@ impl<'a> ComboBox<'a> { .set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left); self } + /// Sets the combo box height. /// /// Default: `ComboBoxHeight::Regular` @@ -113,6 +117,7 @@ impl<'a> ComboBox<'a> { ); self } + /// Sets the combo box preview mode. /// /// Default: `ComboBoxPreviewMode::Full` @@ -128,6 +133,7 @@ impl<'a> ComboBox<'a> { ); self } + /// Creates a combo box and starts appending to it. /// /// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been diff --git a/imgui/src/widget/image.rs b/imgui/src/widget/image.rs index 86b6b0f..b6325c4 100644 --- a/imgui/src/widget/image.rs +++ b/imgui/src/widget/image.rs @@ -18,7 +18,7 @@ pub struct Image { impl Image { /// Creates a new image builder with the given texture and size - pub fn new(texture_id: TextureId, size: [f32; 2]) -> Image { + pub const fn new(texture_id: TextureId, size: [f32; 2]) -> Image { Image { texture_id, size, @@ -29,27 +29,27 @@ impl Image { } } /// Sets the image size - pub fn size(mut self, size: [f32; 2]) -> Self { + pub const fn size(mut self, size: [f32; 2]) -> Self { self.size = size; self } /// Sets uv0 (default `[0.0, 0.0]`) - pub fn uv0(mut self, uv0: [f32; 2]) -> Self { + pub const fn uv0(mut self, uv0: [f32; 2]) -> Self { self.uv0 = uv0; self } /// Sets uv1 (default `[1.0, 1.0]`) - pub fn uv1(mut self, uv1: [f32; 2]) -> Self { + pub const fn uv1(mut self, uv1: [f32; 2]) -> Self { self.uv1 = uv1; self } /// Sets the tint color (default: no tint color) - pub fn tint_col(mut self, tint_col: [f32; 4]) -> Self { + pub const fn tint_col(mut self, tint_col: [f32; 4]) -> Self { self.tint_col = tint_col; self } /// Sets the border color (default: no border) - pub fn border_col(mut self, border_col: [f32; 4]) -> Self { + pub const fn border_col(mut self, border_col: [f32; 4]) -> Self { self.border_col = border_col; self } diff --git a/imgui/src/widget/list_box.rs b/imgui/src/widget/list_box.rs index 1985e75..b58764c 100644 --- a/imgui/src/widget/list_box.rs +++ b/imgui/src/widget/list_box.rs @@ -9,9 +9,7 @@ use crate::Ui; #[derive(Copy, Clone, Debug)] enum Size { - Vec { - size: sys::ImVec2, - }, + Vec(sys::ImVec2), Items { items_count: i32, height_in_items: i32, @@ -27,12 +25,10 @@ pub struct ListBox<'a> { impl<'a> ListBox<'a> { /// Constructs a new list box builder. - pub fn new(label: &'a ImStr) -> ListBox<'a> { + pub const fn new(label: &'a ImStr) -> ListBox<'a> { ListBox { label, - size: Size::Vec { - size: [0.0, 0.0].into(), - }, + size: Size::Vec(sys::ImVec2::zero()), } } /// Sets the list box size based on the number of items that you want to make visible @@ -40,13 +36,14 @@ impl<'a> ListBox<'a> { /// We add +25% worth of item height to allow the user to see at a glance if there are more items up/down, without looking at the scrollbar. /// We don't add this extra bit if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. #[inline] - pub fn calculate_size(mut self, items_count: i32, height_in_items: i32) -> Self { + pub const fn calculate_size(mut self, items_count: i32, height_in_items: i32) -> Self { self.size = Size::Items { items_count, height_in_items, }; self } + /// Sets the list box size based on the given width and height /// If width or height are 0 or smaller, a default value is calculated /// Helper to calculate the size of a listbox and display a label on the right. @@ -54,8 +51,8 @@ impl<'a> ListBox<'a> { /// /// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height #[inline] - pub fn size(mut self, size: [f32; 2]) -> Self { - self.size = Size::Vec { size: size.into() }; + pub const fn size(mut self, size: [f32; 2]) -> Self { + self.size = Size::Vec(sys::ImVec2::new(size[0], size[1])); self } /// Creates a list box and starts appending to it. @@ -68,7 +65,7 @@ impl<'a> ListBox<'a> { pub fn begin(self, ui: &Ui) -> Option { let should_render = unsafe { match self.size { - Size::Vec { size } => sys::igListBoxHeaderVec2(self.label.as_ptr(), size), + Size::Vec(size) => sys::igListBoxHeaderVec2(self.label.as_ptr(), size), Size::Items { items_count, height_in_items, diff --git a/imgui/src/widget/progress_bar.rs b/imgui/src/widget/progress_bar.rs index 3dc002b..d11e1fb 100644 --- a/imgui/src/widget/progress_bar.rs +++ b/imgui/src/widget/progress_bar.rs @@ -31,16 +31,18 @@ impl<'a> ProgressBar<'a> { /// /// The progress bar will be automatically sized to fill the entire width of the window if no /// custom size is specified. - pub fn new(fraction: f32) -> ProgressBar<'a> { + #[inline] + pub const fn new(fraction: f32) -> ProgressBar<'a> { ProgressBar { fraction, size: [-1.0, 0.0], overlay_text: None, } } + /// Sets an optional text that will be drawn over the progress bar. #[inline] - pub fn overlay_text(mut self, overlay_text: &'a ImStr) -> ProgressBar { + pub const fn overlay_text(mut self, overlay_text: &'a ImStr) -> ProgressBar { self.overlay_text = Some(overlay_text); self } @@ -50,10 +52,11 @@ impl<'a> ProgressBar<'a> { /// Negative values will automatically align to the end of the axis, zero will let the progress /// bar choose a size, and positive values will use the given size. #[inline] - pub fn size(mut self, size: [f32; 2]) -> Self { + pub const fn size(mut self, size: [f32; 2]) -> Self { self.size = size; self } + /// Builds the progress bar pub fn build(self, _: &Ui) { unsafe { diff --git a/imgui/src/widget/selectable.rs b/imgui/src/widget/selectable.rs index 6297d7b..3014bf8 100644 --- a/imgui/src/widget/selectable.rs +++ b/imgui/src/widget/selectable.rs @@ -33,7 +33,8 @@ pub struct Selectable<'a> { impl<'a> Selectable<'a> { /// Constructs a new selectable builder. - pub fn new(label: &ImStr) -> Selectable { + #[inline] + pub const fn new(label: &ImStr) -> Selectable { Selectable { label, selected: false, diff --git a/imgui/src/widget/slider.rs b/imgui/src/widget/slider.rs index 28d9490..97e5556 100644 --- a/imgui/src/widget/slider.rs +++ b/imgui/src/widget/slider.rs @@ -182,7 +182,6 @@ pub struct AngleSlider<'a> { impl<'a> AngleSlider<'a> { /// Constructs a new angle slider builder. pub fn new(label: &ImStr) -> AngleSlider { - use crate::im_str; AngleSlider { label, min_degrees: -360.0, diff --git a/imgui/src/widget/tab.rs b/imgui/src/widget/tab.rs index bfa1d5b..e2070f3 100644 --- a/imgui/src/widget/tab.rs +++ b/imgui/src/widget/tab.rs @@ -63,7 +63,8 @@ pub struct TabBar<'a> { } impl<'a> TabBar<'a> { - pub fn new(id: &'a ImStr) -> Self { + #[inline] + pub const fn new(id: &'a ImStr) -> Self { Self { id, flags: TabBarFlags::empty(), diff --git a/imgui/src/widget/tree.rs b/imgui/src/widget/tree.rs index 8d978e8..99c12fe 100644 --- a/imgui/src/widget/tree.rs +++ b/imgui/src/widget/tree.rs @@ -70,18 +70,21 @@ pub enum TreeNodeId<'a> { } impl<'a, T: ?Sized + AsRef> From<&'a T> for TreeNodeId<'a> { + #[inline] fn from(s: &'a T) -> Self { TreeNodeId::Str(s.as_ref()) } } impl From<*const T> for TreeNodeId<'static> { + #[inline] fn from(p: *const T) -> Self { TreeNodeId::Ptr(p as *const c_void) } } impl From<*mut T> for TreeNodeId<'static> { + #[inline] fn from(p: *mut T) -> Self { TreeNodeId::Ptr(p as *const T as *const c_void) } @@ -289,6 +292,7 @@ pub struct TreeNodeToken { impl TreeNodeToken { /// Pops a tree node + #[inline] pub fn pop(mut self, _: &Ui) { if !self.ctx.is_null() { self.ctx = ptr::null();