diff --git a/imgui-examples/examples/hello_world.rs b/imgui-examples/examples/hello_world.rs index 3e423a4..0c87a99 100644 --- a/imgui-examples/examples/hello_world.rs +++ b/imgui-examples/examples/hello_world.rs @@ -4,13 +4,20 @@ mod support; fn main() { let system = support::init(file!()); + let mut value = 0; + let choices = ["test test this is 1", "test test this is 2"]; system.main_loop(move |_, ui| { Window::new(im_str!("Hello world")) .size([300.0, 110.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello world!")); - ui.text(im_str!("こんにちは世界!")); - ui.text(im_str!("This...is...imgui-rs!")); + ui.text_wrapped("Hello world!"); + ui.text_wrapped("こんにちは世界!"); + if ui.button(choices[value]) { + value += 1; + value %= 2; + } + + ui.button("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; ui.text(format!( diff --git a/imgui-sys/src/lib.rs b/imgui-sys/src/lib.rs index 3913d39..d7afc7f 100644 --- a/imgui-sys/src/lib.rs +++ b/imgui-sys/src/lib.rs @@ -18,7 +18,7 @@ // // ¹ The exception to this is that `std::os::raw` isn't there for `no_std`, and // `libc` has potentially undesirable linking impacts on windows. -extern crate chlorine as cty; +pub extern crate chlorine as cty; #[cfg(feature = "wasm")] mod wasm_bindings; diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 601d1f2..eba5448 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -527,7 +527,7 @@ impl Context { Ui { ctx: self, font_atlas, - buffer: RefCell::new(Vec::new()), + buffer: Vec::new().into(), } } } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 65077ea..9b38b8d 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -120,10 +120,43 @@ impl Context { pub struct Ui<'ui> { ctx: &'ui Context, font_atlas: Option>, - buffer: cell::RefCell>, + // imgui isn't mutli-threaded -- so no one will ever access + buffer: cell::UnsafeCell>, } impl<'ui> Ui<'ui> { + /// Internal method to push a single text to our scratch buffer. + fn scratch_txt(&self, txt: impl AsRef) -> *const sys::cty::c_char { + unsafe { + let handle = &mut *self.buffer.get(); + handle.clear(); + handle.extend(txt.as_ref().as_bytes()); + handle.push(b'\0'); + + handle.as_ptr() as *const _ + } + } + + fn scratch_txt_two( + &self, + txt_0: impl AsRef, + txt_1: impl AsRef, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + unsafe { + let handle = &mut *self.buffer.get(); + handle.clear(); + handle.extend(txt_0.as_ref().as_bytes()); + handle.push(b'\0'); + handle.extend(txt_1.as_ref().as_bytes()); + handle.push(b'\0'); + + ( + handle.as_ptr() as *const _, + handle.as_ptr().add(txt_1.as_ref().len() + 1) as *const _, + ) + } + } + /// Returns an immutable reference to the inputs/outputs object #[doc(alias = "GetIO")] pub fn io(&self) -> &Io { diff --git a/imgui/src/widget/color_editors.rs b/imgui/src/widget/color_editors.rs index daa2101..1d8949a 100644 --- a/imgui/src/widget/color_editors.rs +++ b/imgui/src/widget/color_editors.rs @@ -1,7 +1,6 @@ use bitflags::bitflags; use std::ptr; -use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -184,16 +183,16 @@ bitflags! { /// ``` #[derive(Debug)] #[must_use] -pub struct ColorEdit<'a> { - label: &'a ImStr, +pub struct ColorEdit<'a, T: AsRef + 'a> { + label: T, value: EditableColor<'a>, flags: ColorEditFlags, } -impl<'a> ColorEdit<'a> { +impl<'a, T: AsRef + 'a> ColorEdit<'a, T> { /// Constructs a new color editor builder. #[doc(alias = "ColorEdit3", alias = "ColorEdit4")] - pub fn new>>(label: &'a ImStr, value: T) -> ColorEdit<'a> { + pub fn new(label: T, value: impl Into>) -> ColorEdit<'a, T> { ColorEdit { label, value: value.into(), @@ -319,21 +318,21 @@ impl<'a> ColorEdit<'a> { /// Builds the color editor. /// /// Returns true if the color value was changed. - pub fn build(mut self, _: &Ui) -> bool { + pub fn build(mut self, ui: &Ui) -> bool { if let EditableColor::Float3(_) = self.value { self.flags.insert(ColorEditFlags::NO_ALPHA); } match self.value { EditableColor::Float3(value) => unsafe { sys::igColorEdit3( - self.label.as_ptr(), + ui.scratch_txt(self.label), value.as_mut_ptr(), self.flags.bits() as _, ) }, EditableColor::Float4(value) => unsafe { sys::igColorEdit4( - self.label.as_ptr(), + ui.scratch_txt(self.label), value.as_mut_ptr(), self.flags.bits() as _, ) @@ -358,17 +357,17 @@ impl<'a> ColorEdit<'a> { /// ``` #[derive(Debug)] #[must_use] -pub struct ColorPicker<'a> { - label: &'a ImStr, +pub struct ColorPicker<'a, T: AsRef + 'a> { + label: T, value: EditableColor<'a>, flags: ColorEditFlags, ref_color: Option<&'a [f32; 4]>, } -impl<'a> ColorPicker<'a> { +impl<'a, T: AsRef> ColorPicker<'a, T> { /// Constructs a new color picker builder. #[doc(alias = "ColorButton")] - pub fn new>>(label: &'a ImStr, value: T) -> ColorPicker<'a> { + pub fn new(label: T, value: impl Into>) -> Self { ColorPicker { label, value: value.into(), @@ -507,14 +506,14 @@ impl<'a> ColorPicker<'a> { /// Builds the color picker. /// /// Returns true if the color value was changed. - pub fn build(mut self, _: &Ui) -> bool { + pub fn build(mut self, ui: &Ui) -> bool { if let EditableColor::Float3(_) = self.value { self.flags.insert(ColorEditFlags::NO_ALPHA); } let ref_color = self.ref_color.map(|c| c.as_ptr()).unwrap_or(ptr::null()); unsafe { sys::igColorPicker4( - self.label.as_ptr(), + ui.scratch_txt(self.label), self.value.as_mut_ptr(), self.flags.bits() as _, ref_color, @@ -536,16 +535,16 @@ impl<'a> ColorPicker<'a> { /// ``` #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ColorButton<'a> { - desc_id: &'a ImStr, +pub struct ColorButton { + desc_id: T, color: [f32; 4], flags: ColorEditFlags, size: [f32; 2], } -impl<'a> ColorButton<'a> { +impl> ColorButton { /// Constructs a new color button builder. - pub fn new(desc_id: &ImStr, color: [f32; 4]) -> ColorButton { + pub fn new(desc_id: T, color: [f32; 4]) -> Self { ColorButton { desc_id, color, @@ -622,10 +621,10 @@ impl<'a> ColorButton<'a> { /// Builds the color button. /// /// Returns true if this color button was clicked. - pub fn build(self, _: &Ui) -> bool { + pub fn build(self, ui: &Ui) -> bool { unsafe { sys::igColorButton( - self.desc_id.as_ptr(), + ui.scratch_txt(self.desc_id), self.color.into(), self.flags.bits() as _, self.size.into(), diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index 5859c84..a412fc5 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -2,7 +2,6 @@ use bitflags::bitflags; use std::borrow::Cow; use std::ptr; -use crate::string::ImStr; use crate::sys; use crate::Ui; diff --git a/imgui/src/widget/misc.rs b/imgui/src/widget/misc.rs index 152753b..d19fdeb 100644 --- a/imgui/src/widget/misc.rs +++ b/imgui/src/widget/misc.rs @@ -1,7 +1,6 @@ use bitflags::bitflags; use std::ops::{BitAnd, BitAndAssign, BitOrAssign, Not}; -use crate::string::ImStr; use crate::sys; use crate::{Direction, Ui}; @@ -29,7 +28,7 @@ impl<'ui> Ui<'ui> { /// label's width in the current style. /// the current style. #[doc(alias = "Button")] - pub fn button(&self, label: &ImStr) -> bool { + pub fn button(&self, label: impl AsRef) -> bool { self.button_with_size(label, [0.0, 0.0]) } @@ -40,48 +39,53 @@ impl<'ui> Ui<'ui> { /// Setting `size` as `[0.0, 0.0]` will size the button to the label's width in /// the current style. #[doc(alias = "Button")] - pub fn button_with_size(&self, label: &ImStr, size: [f32; 2]) -> bool { - unsafe { sys::igButton(label.as_ptr(), size.into()) } + pub fn button_with_size(&self, label: impl AsRef, size: [f32; 2]) -> bool { + unsafe { sys::igButton(self.scratch_txt(label), size.into()) } } /// Renders a small clickable button that is easy to embed in text. /// /// Returns true if this button was clicked. #[doc(alias = "SmallButton")] - pub fn small_button(&self, label: &ImStr) -> bool { - unsafe { sys::igSmallButton(label.as_ptr()) } + pub fn small_button(&self, label: impl AsRef) -> bool { + unsafe { sys::igSmallButton(self.scratch_txt(label)) } } /// Renders a widget with button behaviour without the visual look. /// /// Returns true if this button was clicked. #[doc(alias = "InvisibleButton")] - pub fn invisible_button(&self, id: &ImStr, size: [f32; 2]) -> bool { - unsafe { sys::igInvisibleButton(id.as_ptr(), size.into(), 0) } + pub fn invisible_button(&self, id: impl AsRef, size: [f32; 2]) -> bool { + unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), 0) } } /// Renders a widget with button behaviour without the visual look. /// /// Returns true if this button was clicked. #[doc(alias = "InvisibleButton")] - pub fn invisible_button_flags(&self, id: &ImStr, size: [f32; 2], flags: ButtonFlags) -> bool { - unsafe { sys::igInvisibleButton(id.as_ptr(), size.into(), flags.bits() as i32) } + pub fn invisible_button_flags( + &self, + id: impl AsRef, + size: [f32; 2], + flags: ButtonFlags, + ) -> bool { + unsafe { sys::igInvisibleButton(self.scratch_txt(id), size.into(), flags.bits() as i32) } } /// Renders a square button with an arrow shape. /// /// Returns true if this button was clicked. #[doc(alias = "ArrowButton")] - pub fn arrow_button(&self, id: &ImStr, direction: Direction) -> bool { - unsafe { sys::igArrowButton(id.as_ptr(), direction as i32) } + pub fn arrow_button(&self, id: impl AsRef, direction: Direction) -> bool { + unsafe { sys::igArrowButton(self.scratch_txt(id), direction as i32) } } /// Renders a simple checkbox. /// /// Returns true if this checkbox was clicked. #[doc(alias = "Checkbox")] - pub fn checkbox(&self, label: &ImStr, value: &mut bool) -> bool { - unsafe { sys::igCheckbox(label.as_ptr(), value as *mut bool) } + pub fn checkbox(&self, label: impl AsRef, value: &mut bool) -> bool { + unsafe { sys::igCheckbox(self.scratch_txt(label), value as *mut bool) } } /// Renders a checkbox suitable for toggling bit flags using a mask. /// /// Returns true if this checkbox was clicked. - pub fn checkbox_flags(&self, label: &ImStr, flags: &mut T, mask: T) -> bool + pub fn checkbox_flags(&self, label: impl AsRef, flags: &mut T, mask: T) -> bool where T: Copy + PartialEq + BitOrAssign + BitAndAssign + BitAnd + Not, { @@ -100,14 +104,14 @@ impl<'ui> Ui<'ui> { /// /// Returns true if this radio button was clicked. #[doc(alias = "RadioButtonBool")] - pub fn radio_button_bool(&self, label: &ImStr, active: bool) -> bool { - unsafe { sys::igRadioButtonBool(label.as_ptr(), active) } + pub fn radio_button_bool(&self, label: impl AsRef, active: bool) -> bool { + unsafe { sys::igRadioButtonBool(self.scratch_txt(label), active) } } /// Renders a radio button suitable for choosing an arbitrary value. /// /// Returns true if this radio button was clicked. #[doc(alias = "RadioButtonBool")] - pub fn radio_button(&self, label: &ImStr, value: &mut T, button_value: T) -> bool + pub fn radio_button(&self, label: impl AsRef, value: &mut T, button_value: T) -> bool where T: Copy + PartialEq, { diff --git a/imgui/src/widget/text.rs b/imgui/src/widget/text.rs index f64d4ae..65f3849 100644 --- a/imgui/src/widget/text.rs +++ b/imgui/src/widget/text.rs @@ -39,26 +39,13 @@ impl<'ui> Ui<'ui> { /// Renders text wrapped to the end of window (or column) #[doc(alias = "TextWrapperd")] pub fn text_wrapped(&self, text: impl AsRef) { - let mut handle = self.buffer.borrow_mut(); - handle.clear(); - handle.extend(text.as_ref().as_bytes()); - handle.push(b'\0'); - unsafe { sys::igTextWrapped(fmt_ptr(), handle.as_ptr()) } + unsafe { sys::igTextWrapped(fmt_ptr(), self.scratch_txt(text)) } } /// Render a text + label combination aligned the same way as value+label widgets #[doc(alias = "LabelText")] pub fn label_text(&self, label: impl AsRef, text: impl AsRef) { - let mut handle = self.buffer.borrow_mut(); - handle.clear(); - handle.extend(label.as_ref().as_bytes()); - handle.push(b'\0'); - handle.extend(text.as_ref().as_bytes()); - handle.push(b'\0'); - - let ptr_one = handle.as_ptr(); - let ptr_two = unsafe { ptr_one.add(text.as_ref().len() + 1) }; - - unsafe { sys::igLabelText(ptr_one as *const _, fmt_ptr(), ptr_two as *const _) } + let (ptr_one, ptr_two) = self.scratch_txt_two(label, text); + unsafe { sys::igLabelText(ptr_one, fmt_ptr(), ptr_two) } } /// Renders text with a little bullet aligned to the typical tree node #[doc(alias = "BulletText")]