Constify a large number of fns

This commit is contained in:
Thom Chiovoloni 2021-02-01 02:50:47 -08:00
parent 4f1cde06f2
commit 59970d670e
21 changed files with 235 additions and 77 deletions

View File

@ -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

View File

@ -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<ImVec2> 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<ImVec2> 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<ImVec4> 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<ImVec4> 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)
}
}

View File

@ -23,6 +23,7 @@ pub(crate) struct ClipboardContext {
}
impl ClipboardContext {
#[inline]
pub fn new(backend: Box<dyn ClipboardBackend>) -> ClipboardContext {
ClipboardContext {
backend,

View File

@ -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());

View File

@ -12,6 +12,7 @@ pub struct ImVector<T> {
}
impl<T> ImVector<T> {
#[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<T>: 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<T>: 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<T>: 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<T>: 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<T: Copy> {
}
impl<T: Copy> InclusiveRangeBounds<T> for RangeFrom<T> {
#[inline]
fn start_bound(&self) -> Option<&T> {
Some(&self.start)
}
#[inline]
fn end_bound(&self) -> Option<&T> {
None
}
}
impl<T: Copy> InclusiveRangeBounds<T> for RangeInclusive<T> {
#[inline]
fn start_bound(&self) -> Option<&T> {
Some(self.start())
}
#[inline]
fn end_bound(&self) -> Option<&T> {
Some(self.end())
}
}
impl<T: Copy> InclusiveRangeBounds<T> for RangeToInclusive<T> {
#[inline]
fn start_bound(&self) -> Option<&T> {
None
}
#[inline]
fn end_bound(&self) -> Option<&T> {
Some(&self.end)
}

View File

@ -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<i32> for Id<'static> {
#[inline]
fn from(i: i32) -> Self {
Id::Int(i)
}
}
impl<'a, T: ?Sized + AsRef<str>> From<&'a T> for Id<'a> {
#[inline]
fn from(s: &'a T) -> Self {
Id::Str(s.as_ref())
}
}
impl<T> From<*const T> for Id<'static> {
#[inline]
fn from(p: *const T) -> Self {
Id::Ptr(p as *const c_void)
}
}
impl<T> From<*mut T> for Id<'static> {
#[inline]
fn from(p: *mut T) -> Self {
Id::Ptr(p as *const T as *const c_void)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -36,6 +36,7 @@ unsafe impl RawCast<sys::ImDrawData> 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::Item> {
self.iter.next().map(|cmd| {
let cmd_params = DrawCmdParams {

View File

@ -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<usize> for TextureId {
#[inline]
fn from(id: usize) -> Self {
TextureId(id)
}
}
impl<T> From<*const T> for TextureId {
#[inline]
fn from(ptr: *const T) -> Self {
TextureId(ptr as usize)
}
}
impl<T> From<*mut T> for TextureId {
#[inline]
fn from(ptr: *mut T) -> Self {
TextureId(ptr as usize)
}
@ -56,6 +61,8 @@ pub struct Textures<T> {
}
impl<T> Textures<T> {
// TODO: hasher like rustc_hash::FxHashMap or something would let this be
// `const fn`
pub fn new() -> Self {
Textures {
textures: HashMap::new(),

View File

@ -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<u8>);
@ -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<u8>) -> 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<u8>) -> 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<String> for ImString {
#[inline]
fn from(s: String) -> ImString {
ImString::new(s)
}
}
impl<'a> From<ImString> 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<ImStr>> From<&'a T> for ImString {
#[inline]
fn from(s: &'a T) -> ImString {
s.as_ref().to_owned()
}
}
impl AsRef<ImStr> for ImString {
#[inline]
fn as_ref(&self) -> &ImStr {
self
}
}
impl Borrow<ImStr> for ImString {
#[inline]
fn borrow(&self) -> &ImStr {
self
}
}
impl AsRef<str> for ImString {
#[inline]
fn as_ref(&self) -> &str {
self.to_str()
}
}
impl Borrow<str> for ImString {
#[inline]
fn borrow(&self) -> &str {
self.to_str()
}
@ -179,18 +227,21 @@ impl Borrow<str> for ImString {
impl Index<RangeFull> 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<CStr> 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<ImStr> for ImStr {
#[inline]
fn as_ref(&self) -> &ImStr {
self
}
}
impl AsRef<str> 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())
}
}

View File

@ -182,12 +182,14 @@ impl Style {
impl Index<StyleColor> for Style {
type Output = [f32; 4];
#[inline]
fn index(&self, index: StyleColor) -> &[f32; 4] {
&self.colors[index as usize]
}
}
impl IndexMut<StyleColor> for Style {
#[inline]
fn index_mut(&mut self, index: StyleColor) -> &mut [f32; 4] {
&mut self.colors[index as usize]
}

View File

@ -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

View File

@ -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
}

View File

@ -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<ListBoxToken> {
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,

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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(),

View File

@ -70,18 +70,21 @@ pub enum TreeNodeId<'a> {
}
impl<'a, T: ?Sized + AsRef<ImStr>> From<&'a T> for TreeNodeId<'a> {
#[inline]
fn from(s: &'a T) -> Self {
TreeNodeId::Str(s.as_ref())
}
}
impl<T> From<*const T> for TreeNodeId<'static> {
#[inline]
fn from(p: *const T) -> Self {
TreeNodeId::Ptr(p as *const c_void)
}
}
impl<T> 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();