From 52e08bd09a3ba2d4cbf39c1bac11fc86dc820e73 Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Mon, 6 Sep 2021 23:15:46 -0700 Subject: [PATCH 01/21] init effort --- imgui/src/context.rs | 1 + imgui/src/lib.rs | 1 + imgui/src/widget/text.rs | 22 ++++++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 433dc41..601d1f2 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -527,6 +527,7 @@ impl Context { Ui { ctx: self, font_atlas, + buffer: RefCell::new(Vec::new()), } } } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 8bfe9bf..65077ea 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -120,6 +120,7 @@ impl Context { pub struct Ui<'ui> { ctx: &'ui Context, font_atlas: Option>, + buffer: cell::RefCell>, } impl<'ui> Ui<'ui> { diff --git a/imgui/src/widget/text.rs b/imgui/src/widget/text.rs index 53fab53..f64d4ae 100644 --- a/imgui/src/widget/text.rs +++ b/imgui/src/widget/text.rs @@ -38,13 +38,27 @@ impl<'ui> Ui<'ui> { } /// Renders text wrapped to the end of window (or column) #[doc(alias = "TextWrapperd")] - pub fn text_wrapped(&self, text: &ImStr) { - unsafe { sys::igTextWrapped(fmt_ptr(), text.as_ptr()) } + 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()) } } /// Render a text + label combination aligned the same way as value+label widgets #[doc(alias = "LabelText")] - pub fn label_text(&self, label: &ImStr, text: &ImStr) { - unsafe { sys::igLabelText(label.as_ptr(), fmt_ptr(), text.as_ptr()) } + 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 _) } } /// Renders text with a little bullet aligned to the typical tree node #[doc(alias = "BulletText")] From 5802931a883acc4461784496607cbe0b2f6102db Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Tue, 7 Sep 2021 00:06:36 -0700 Subject: [PATCH 02/21] did some work and began converting modules over --- imgui-examples/examples/hello_world.rs | 13 +++++++-- imgui-sys/src/lib.rs | 2 +- imgui/src/context.rs | 2 +- imgui/src/lib.rs | 35 +++++++++++++++++++++- imgui/src/widget/color_editors.rs | 39 ++++++++++++------------- imgui/src/widget/combo_box.rs | 1 - imgui/src/widget/misc.rs | 40 ++++++++++++++------------ imgui/src/widget/text.rs | 19 ++---------- 8 files changed, 90 insertions(+), 61 deletions(-) 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")] From 08d285d029c13696fee16de1a9a351832bed231d Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Tue, 7 Sep 2021 11:55:56 -0700 Subject: [PATCH 03/21] added more bounds, somewhat chaotically --- imgui-examples/examples/test_window_impl.rs | 4 +- imgui/src/widget/combo_box.rs | 50 ++++++++++----------- imgui/src/widget/list_box.rs | 23 +++++----- imgui/src/widget/selectable.rs | 11 +++-- 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 867b848..211a699 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -480,7 +480,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.separator(); ui.label_text(im_str!("label"), im_str!("Value")); - ComboBox::new(im_str!("combo")).build_simple_string(ui, + ComboBox::new(&"combo").build_simple_string(ui, &mut state.item, &[ im_str!("aaaa"), @@ -515,7 +515,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { im_str!("Tilefish"), ]; - ListBox::new(im_str!("selectables list")).build(ui, || { + ListBox::new("selectables list").build(ui, || { for (index, name) in names.iter().enumerate() { let selected = matches!(state.selected_fish2, Some(i) if i == index ); if Selectable::new(name).selected(selected).build(ui) { diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index a412fc5..e321f83 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -1,6 +1,5 @@ use bitflags::bitflags; use std::borrow::Cow; -use std::ptr; use crate::sys; use crate::Ui; @@ -55,16 +54,16 @@ pub struct ComboBoxFlags: u32 { /// Builder for a combo box widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ComboBox<'a> { - label: &'a ImStr, - preview_value: Option<&'a ImStr>, +pub struct ComboBox { + label: T, + preview_value: Option

, flags: ComboBoxFlags, } -impl<'a> ComboBox<'a> { +impl<'a, T: AsRef, P: AsRef> ComboBox { /// Constructs a new combo box builder. #[doc(alias = "BeginCombo")] - pub const fn new(label: &'a ImStr) -> ComboBox<'a> { + pub fn new(label: T) -> Self { ComboBox { label, preview_value: None, @@ -74,14 +73,14 @@ impl<'a> ComboBox<'a> { /// Sets the preview value displayed in the preview box (if visible). #[inline] - pub const fn preview_value(mut self, preview_value: &'a ImStr) -> Self { + pub fn preview_value(mut self, preview_value: P) -> Self { self.preview_value = Some(preview_value); self } /// Replaces all current settings with the given flags. #[inline] - pub const fn flags(mut self, flags: ComboBoxFlags) -> Self { + pub fn flags(mut self, flags: ComboBoxFlags) -> Self { self.flags = flags; self } @@ -141,11 +140,13 @@ impl<'a> ComboBox<'a> { #[must_use] pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option> { let should_render = unsafe { - sys::igBeginCombo( - self.label.as_ptr(), - self.preview_value.map(ImStr::as_ptr).unwrap_or(ptr::null()), - self.flags.bits() as i32, - ) + if let Some(preview_value) = self.preview_value { + let (ptr_one, ptr_two) = ui.scratch_txt_two(self.label, preview_value); + sys::igBeginCombo(ptr_one, ptr_two, self.flags.bits() as i32) + } else { + let ptr_one = ui.scratch_txt(self.label); + sys::igBeginCombo(ptr_one, std::ptr::null(), self.flags.bits() as i32) + } }; if should_render { Some(ComboBoxToken::new(ui)) @@ -157,7 +158,7 @@ impl<'a> ComboBox<'a> { /// Returns the result of the closure, if it is called. /// /// Note: the closure is not called if the combo box is not open. - pub fn build T>(self, ui: &Ui, f: F) -> Option { + pub fn build R>(self, ui: &Ui, f: F) -> Option { self.begin(ui).map(|_combo| f()) } } @@ -172,29 +173,28 @@ create_token!( ); /// # Convenience functions -impl<'a> ComboBox<'a> { +impl<'a, T: AsRef + Clone> ComboBox> { /// Builds a simple combo box for choosing from a slice of values #[doc(alias = "BeginCombo")] - pub fn build_simple( - self, + pub fn build_simple( + mut self, ui: &Ui, current_item: &mut usize, - items: &[T], + items: &'a [V], label_fn: &L, ) -> bool where - for<'b> L: Fn(&'b T) -> Cow<'b, ImStr>, + for<'b> L: Fn(&'b V) -> Cow<'b, str>, { use crate::widget::selectable::Selectable; let mut result = false; - let mut cb = self; let preview_value = items.get(*current_item).map(label_fn); - if cb.preview_value.is_none() { - if let Some(preview_value) = preview_value.as_ref() { - cb = cb.preview_value(preview_value); + if self.preview_value.is_none() { + if let Some(preview_value) = preview_value { + self = self.preview_value(preview_value); } } - if let Some(_cb) = cb.begin(ui) { + if let Some(_cb) = self.begin(ui) { for (idx, item) in items.iter().enumerate() { let text = label_fn(item); let selected = idx == *current_item; @@ -213,7 +213,7 @@ impl<'a> ComboBox<'a> { #[doc(alias = "BeginCombo")] pub fn build_simple_string(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool where - S: AsRef + ?Sized, + S: AsRef + ?Sized, { self.build_simple(ui, current_item, items, &|&s| s.as_ref().into()) } diff --git a/imgui/src/widget/list_box.rs b/imgui/src/widget/list_box.rs index 60482f9..70fbce5 100644 --- a/imgui/src/widget/list_box.rs +++ b/imgui/src/widget/list_box.rs @@ -1,21 +1,20 @@ use std::borrow::Cow; -use crate::string::ImStr; use crate::sys; use crate::Ui; /// Builder for a list box widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ListBox<'a> { - label: &'a ImStr, +pub struct ListBox { + label: T, size: sys::ImVec2, } -impl<'a> ListBox<'a> { +impl> ListBox { /// Constructs a new list box builder. #[doc(alias = "ListBoxHeaderVec2", alias = "ListBoxHeaderInt")] - pub const fn new(label: &'a ImStr) -> ListBox<'a> { + pub fn new(label: T) -> ListBox { ListBox { label, size: sys::ImVec2::zero(), @@ -29,7 +28,7 @@ impl<'a> ListBox<'a> { /// /// Default: [0.0, 0.0], in which case the combobox calculates a sensible width and height #[inline] - pub const fn size(mut self, size: [f32; 2]) -> Self { + pub fn size(mut self, size: [f32; 2]) -> Self { self.size = sys::ImVec2::new(size[0], size[1]); self } @@ -41,7 +40,7 @@ impl<'a> ListBox<'a> { /// Returns `None` if the list box is not open and no content should be rendered. #[must_use] pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option> { - let should_render = unsafe { sys::igBeginListBox(self.label.as_ptr(), self.size) }; + let should_render = unsafe { sys::igBeginListBox(ui.scratch_txt(self.label), self.size) }; if should_render { Some(ListBoxToken::new(ui)) } else { @@ -52,7 +51,7 @@ impl<'a> ListBox<'a> { /// Returns the result of the closure, if it is called. /// /// Note: the closure is not called if the list box is not open. - pub fn build T>(self, ui: &Ui<'_>, f: F) -> Option { + pub fn build R>(self, ui: &Ui<'_>, f: F) -> Option { self.begin(ui).map(|_list| f()) } } @@ -67,17 +66,17 @@ create_token!( ); /// # Convenience functions -impl<'a> ListBox<'a> { +impl> ListBox { /// Builds a simple list box for choosing from a slice of values - pub fn build_simple( + pub fn build_simple( self, ui: &Ui, current_item: &mut usize, - items: &[T], + items: &[V], label_fn: &L, ) -> bool where - for<'b> L: Fn(&'b T) -> Cow<'b, ImStr>, + for<'b> L: Fn(&'b V) -> Cow<'b, str>, { use crate::widget::selectable::Selectable; let mut result = false; diff --git a/imgui/src/widget/selectable.rs b/imgui/src/widget/selectable.rs index 398fe37..83fd455 100644 --- a/imgui/src/widget/selectable.rs +++ b/imgui/src/widget/selectable.rs @@ -1,6 +1,5 @@ use bitflags::bitflags; -use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -25,7 +24,7 @@ bitflags!( #[derive(Copy, Clone, Debug)] #[must_use] pub struct Selectable<'a> { - label: &'a ImStr, + label: &'a str, selected: bool, flags: SelectableFlags, size: [f32; 2], @@ -35,9 +34,9 @@ impl<'a> Selectable<'a> { /// Constructs a new selectable builder. #[inline] #[doc(alias = "Selectable")] - pub const fn new(label: &ImStr) -> Selectable { + pub fn new(label: &'a impl AsRef) -> Selectable<'a> { Selectable { - label, + label: label.as_ref(), selected: false, flags: SelectableFlags::empty(), size: [0.0, 0.0], @@ -108,10 +107,10 @@ impl<'a> Selectable<'a> { /// Builds the selectable. /// /// Returns true if the selectable was clicked. - pub fn build(self, _: &Ui) -> bool { + pub fn build(self, ui: &Ui) -> bool { unsafe { sys::igSelectableBool( - self.label.as_ptr(), + ui.scratch_txt(self.label), self.selected, self.flags.bits() as i32, self.size.into(), From 540aa5f292bfd58374552a876add4ab745442e4f Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Tue, 7 Sep 2021 22:44:39 -0700 Subject: [PATCH 04/21] converted combo_box and drag --- imgui-examples/examples/test_window_impl.rs | 13 +- imgui/src/lib.rs | 25 ++++ imgui/src/widget/combo_box.rs | 152 +++++++++++++++----- imgui/src/widget/drag.rs | 32 ++--- 4 files changed, 167 insertions(+), 55 deletions(-) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 211a699..079a9ad 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -480,7 +480,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.separator(); ui.label_text(im_str!("label"), im_str!("Value")); - ComboBox::new(&"combo").build_simple_string(ui, + ui.combo_simple_string("combo", &mut state.item, &[ im_str!("aaaa"), @@ -502,8 +502,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { im_str!("JJJJ"), im_str!("KKKK"), ]; - ComboBox::new(im_str!("combo scroll")).build_simple_string(ui, &mut state.item2, &items); - + ui.combo_simple_string("combo scroll", &mut state.item2, &items); ui.list_box(im_str!("list"), &mut state.item3, &items, 8); @@ -540,7 +539,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { .hint(im_str!("enter text here")) .build(); ui.input_int(im_str!("input int"), &mut state.i0).build(); - Drag::new(im_str!("drag int")).build(ui, &mut state.i0); + // Drag::new(im_str!("drag int")).build(ui, &mut state.i0); ui.input_float(im_str!("input float"), &mut state.f0) .step(0.01) .step_fast(1.0) @@ -808,7 +807,7 @@ CTRL+click on individual component to input value.\n", ); let items = &[im_str!("aaaa"), im_str!("bbbb"), im_str!("cccc"), im_str!("dddd"), im_str!("eeee")]; - ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.stacked_modals_item, items); + ui.combo_simple_string("Combo", &mut state.stacked_modals_item, items); ColorEdit::new(im_str!("color"), &mut state.stacked_modals_color).build(ui); @@ -904,8 +903,8 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { ui.input_float(im_str!("Input"), &mut state.f) .step(0.1) .build(); - let items = [im_str!("Yes"), im_str!("No"), im_str!("Maybe")]; - ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.n, &items); + let items = ["Yes", "No", "Maybe"]; + ui.combo_simple_string("Combo", &mut state.n, &items); ui.checkbox(im_str!("Check"), &mut state.b); menu.end(); } diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 9b38b8d..82a2882 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -157,6 +157,31 @@ impl<'ui> Ui<'ui> { } } + fn scratch_txt_with_opt( + &self, + txt_0: impl AsRef, + txt_1: Option>, + ) -> (*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'); + + if let Some(txt_1) = txt_1 { + 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 _, + ) + } else { + (handle.as_ptr() as *const _, std::ptr::null()) + } + } + } + /// Returns an immutable reference to the inputs/outputs object #[doc(alias = "GetIO")] pub fn io(&self) -> &Io { diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index e321f83..4debebf 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -54,7 +54,7 @@ pub struct ComboBoxFlags: u32 { /// Builder for a combo box widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ComboBox { +pub struct ComboBox { label: T, preview_value: Option

, flags: ComboBoxFlags, @@ -63,21 +63,14 @@ pub struct ComboBox { impl<'a, T: AsRef, P: AsRef> ComboBox { /// Constructs a new combo box builder. #[doc(alias = "BeginCombo")] - pub fn new(label: T) -> Self { + pub fn new(label: T, preview_value: Option

) -> Self { ComboBox { label, - preview_value: None, + preview_value, flags: ComboBoxFlags::empty(), } } - /// Sets the preview value displayed in the preview box (if visible). - #[inline] - pub fn preview_value(mut self, preview_value: P) -> 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 { @@ -173,48 +166,143 @@ create_token!( ); /// # Convenience functions -impl<'a, T: AsRef + Clone> ComboBox> { - /// Builds a simple combo box for choosing from a slice of values +impl<'ui> Ui<'ui> { + /// Creates a combo box which can be appended to with `Selectable::new`. + /// + /// If you do not want to provide a preview, use [begin_combo_no_preview]. If you want + /// to pass flags, use [begin_combo_with_flags]. + /// + /// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been + /// rendered, the token must be ended by calling `.end()`. + /// + /// Returns `None` if the combo box is not open and no content should be rendered. + #[must_use] #[doc(alias = "BeginCombo")] - pub fn build_simple( - mut self, - ui: &Ui, + pub fn begin_combo( + &self, + label: impl AsRef, + preview_value: impl AsRef, + ) -> Option> { + self.begin_combo_with_flags(label, preview_value, ComboBoxFlags::empty()) + } + + /// Creates a combo box which can be appended to with `Selectable::new`. + /// + /// If you do not want to provide a preview, use [begin_combo_no_preview]. + /// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been + /// rendered, the token must be ended by calling `.end()`. + /// + /// Returns `None` if the combo box is not open and no content should be rendered. + #[must_use] + #[doc(alias = "BeginCombo")] + pub fn begin_combo_with_flags( + &self, + label: impl AsRef, + preview_value: impl AsRef, + flags: ComboBoxFlags, + ) -> Option> { + self._begin_combo(label, Some(preview_value), flags) + } + + /// Creates a combo box which can be appended to with `Selectable::new`. + /// + /// If you want to provide a preview, use [begin_combo]. If you want + /// to pass flags, use [begin_combo_no_preview_with_flags]. + /// + /// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been + /// rendered, the token must be ended by calling `.end()`. + /// + /// Returns `None` if the combo box is not open and no content should be rendered. + #[must_use] + #[doc(alias = "BeginCombo")] + pub fn begin_combo_no_preview(&self, label: impl AsRef) -> Option> { + self.begin_combo_no_preview_with_flags(label, ComboBoxFlags::empty()) + } + + /// Creates a combo box which can be appended to with `Selectable::new`. + /// + /// If you do not want to provide a preview, use [begin_combo_no_preview]. + /// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been + /// rendered, the token must be ended by calling `.end()`. + /// + /// Returns `None` if the combo box is not open and no content should be rendered. + #[must_use] + #[doc(alias = "BeginCombo")] + pub fn begin_combo_no_preview_with_flags( + &self, + label: impl AsRef, + flags: ComboBoxFlags, + ) -> Option> { + self._begin_combo(label, Option::<&'static str>::None, flags) + } + + /// This is the internal begin combo method that they all...eventually call. + fn _begin_combo( + &self, + label: impl AsRef, + preview_value: Option>, + flags: ComboBoxFlags, + ) -> Option> { + let should_render = unsafe { + let (ptr_one, ptr_two) = self.scratch_txt_with_opt(label, preview_value); + sys::igBeginCombo(ptr_one, ptr_two, flags.bits() as i32) + }; + if should_render { + Some(ComboBoxToken::new(self)) + } else { + None + } + } + /// Builds a simple combo box for choosing from a slice of values + #[doc(alias = "Combo")] + pub fn combo( + &self, + label: impl AsRef, current_item: &mut usize, - items: &'a [V], - label_fn: &L, + items: &[V], + label_fn: L, ) -> bool where for<'b> L: Fn(&'b V) -> Cow<'b, str>, { use crate::widget::selectable::Selectable; + let label_fn = &label_fn; let mut result = false; let preview_value = items.get(*current_item).map(label_fn); - if self.preview_value.is_none() { - if let Some(preview_value) = preview_value { - self = self.preview_value(preview_value); - } - } - if let Some(_cb) = self.begin(ui) { + + if let Some(_cb) = self._begin_combo(label, preview_value, ComboBoxFlags::empty()) { for (idx, item) in items.iter().enumerate() { let text = label_fn(item); let selected = idx == *current_item; - if Selectable::new(&text).selected(selected).build(ui) { + if Selectable::new(&text).selected(selected).build(self) { *current_item = idx; result = true; } if selected { - ui.set_item_default_focus(); + self.set_item_default_focus(); } } } result } - /// Builds a simple combo box for choosing from a slice of strings - #[doc(alias = "BeginCombo")] - pub fn build_simple_string(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool - where - S: AsRef + ?Sized, - { - self.build_simple(ui, current_item, items, &|&s| s.as_ref().into()) + + /// Builds a simple combo box for choosing from a slice of values + #[doc(alias = "Combo")] + pub fn combo_simple_string( + &self, + label: impl AsRef, + current_item: &mut usize, + items: &[impl AsRef], + ) -> bool { + self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref())) } + + // /// Builds a simple combo box for choosing from a slice of strings + // #[doc(alias = "BeginCombo")] + // pub fn build_simple_string(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool + // where + // S: AsRef + ?Sized, + // { + // self.build_simple(ui, current_item, items, &|&s| s.as_ref().into()) + // } } diff --git a/imgui/src/widget/drag.rs b/imgui/src/widget/drag.rs index dd5cb43..3737220 100644 --- a/imgui/src/widget/drag.rs +++ b/imgui/src/widget/drag.rs @@ -10,19 +10,19 @@ use crate::Ui; /// Builder for a drag slider widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct Drag<'a, T: DataTypeKind> { - label: &'a ImStr, +pub struct Drag<'a, T, L> { + label: L, speed: f32, min: Option, max: Option, - display_format: Option<&'a ImStr>, + display_format: Option<&'a str>, flags: SliderFlags, } -impl<'a, T: DataTypeKind> Drag<'a, T> { +impl<'a, L: AsRef, T: DataTypeKind> Drag<'a, T, L> { /// Constructs a new drag slider builder. #[doc(alias = "DragScalar", alias = "DragScalarN")] - pub fn new(label: &ImStr) -> Drag { + pub fn new(label: L) -> Self { Drag { label, speed: 1.0, @@ -49,7 +49,7 @@ impl<'a, T: DataTypeKind> Drag<'a, T> { } /// Sets the display format using *a C-style printf string* #[inline] - pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + pub fn display_format(mut self, display_format: &'a str) -> Self { self.display_format = Some(display_format); self } @@ -62,10 +62,12 @@ impl<'a, T: DataTypeKind> Drag<'a, T> { /// Builds a drag slider that is bound to the given value. /// /// Returns true if the slider value was changed. - pub fn build(self, _: &Ui, value: &mut T) -> bool { + pub fn build(self, ui: &Ui, value: &mut T) -> bool { unsafe { + let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format); + sys::igDragScalar( - self.label.as_ptr(), + one, T::KIND as i32, value as *mut T as *mut c_void, self.speed, @@ -77,9 +79,7 @@ impl<'a, T: DataTypeKind> Drag<'a, T> { .as_ref() .map(|max| max as *const T) .unwrap_or(ptr::null()) as *const c_void, - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + two, self.flags.bits() as i32, ) } @@ -87,10 +87,12 @@ impl<'a, T: DataTypeKind> Drag<'a, T> { /// Builds a horizontal array of multiple drag sliders attached to the given slice. /// /// Returns true if any slider value was changed. - pub fn build_array(self, _: &Ui, values: &mut [T]) -> bool { + pub fn build_array(self, ui: &Ui, values: &mut [T]) -> bool { unsafe { + let (one, two) = ui.scratch_txt_with_opt(self.label, self.display_format); + sys::igDragScalarN( - self.label.as_ptr(), + one, T::KIND as i32, values.as_mut_ptr() as *mut c_void, values.len() as i32, @@ -103,9 +105,7 @@ impl<'a, T: DataTypeKind> Drag<'a, T> { .as_ref() .map(|max| max as *const T) .unwrap_or(ptr::null()) as *const c_void, - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + two, self.flags.bits() as i32, ) } From d490093daebe9121852513143a6393975aefd044 Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Tue, 7 Sep 2021 22:53:39 -0700 Subject: [PATCH 05/21] finished menu --- imgui-examples/examples/test_window_impl.rs | 26 ++++-------- imgui/src/widget/menu.rs | 44 ++++++++++----------- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 079a9ad..5e88788 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -837,23 +837,15 @@ fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) { menu.end(); } if let Some(menu) = ui.begin_menu(im_str!("Edit")) { - MenuItem::new(im_str!("Undo")) - .shortcut(im_str!("CTRL+Z")) - .build(ui); + MenuItem::new(im_str!("Undo")).shortcut("CTRL+Z").build(ui); MenuItem::new(im_str!("Redo")) - .shortcut(im_str!("CTRL+Y")) + .shortcut("CTRL+Y") .enabled(false) .build(ui); ui.separator(); - MenuItem::new(im_str!("Cut")) - .shortcut(im_str!("CTRL+X")) - .build(ui); - MenuItem::new(im_str!("Copy")) - .shortcut(im_str!("CTRL+C")) - .build(ui); - MenuItem::new(im_str!("Paste")) - .shortcut(im_str!("CTRL+V")) - .build(ui); + MenuItem::new(im_str!("Cut")).shortcut("CTRL+X").build(ui); + MenuItem::new(im_str!("Copy")).shortcut("CTRL+C").build(ui); + MenuItem::new(im_str!("Paste")).shortcut("CTRL+V").build(ui); menu.end(); } menu_bar.end(); @@ -865,9 +857,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { .enabled(false) .build(ui); MenuItem::new(im_str!("New")).build(ui); - MenuItem::new(im_str!("Open")) - .shortcut(im_str!("Ctrl+O")) - .build(ui); + MenuItem::new(im_str!("Open")).shortcut("Ctrl+O").build(ui); if let Some(menu) = ui.begin_menu(im_str!("Open Recent")) { MenuItem::new(im_str!("fish_hat.c")).build(ui); MenuItem::new(im_str!("fish_hat.inl")).build(ui); @@ -884,7 +874,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { menu.end(); } MenuItem::new(im_str!("Save")) - .shortcut(im_str!("Ctrl+S")) + .shortcut("Ctrl+S") .build(ui); MenuItem::new(im_str!("Save As..")).build(ui); ui.separator(); @@ -919,7 +909,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { .is_none()); MenuItem::new(im_str!("Checked")).selected(true).build(ui); MenuItem::new(im_str!("Quit")) - .shortcut(im_str!("Alt+F4")) + .shortcut("Alt+F4") .build(ui); } diff --git a/imgui/src/widget/menu.rs b/imgui/src/widget/menu.rs index 4aba9a0..76a428a 100644 --- a/imgui/src/widget/menu.rs +++ b/imgui/src/widget/menu.rs @@ -1,6 +1,4 @@ -use std::ptr; - -use crate::string::ImStr; +// use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -66,7 +64,7 @@ impl<'ui> Ui<'ui> { /// with `enabled` set to `true`. #[must_use] #[doc(alias = "BeginMenu")] - pub fn begin_menu(&self, label: &ImStr) -> Option> { + pub fn begin_menu(&self, label: impl AsRef) -> Option> { self.begin_menu_with_enabled(label, true) } @@ -78,8 +76,12 @@ impl<'ui> Ui<'ui> { /// Returns `None` if the menu is not visible and no content should be rendered. #[must_use] #[doc(alias = "BeginMenu")] - pub fn begin_menu_with_enabled(&self, label: &ImStr, enabled: bool) -> Option> { - if unsafe { sys::igBeginMenu(label.as_ptr(), enabled) } { + pub fn begin_menu_with_enabled( + &self, + label: impl AsRef, + enabled: bool, + ) -> Option> { + if unsafe { sys::igBeginMenu(self.scratch_txt(label), enabled) } { Some(MenuToken::new(self)) } else { None @@ -92,7 +94,7 @@ impl<'ui> Ui<'ui> { /// This is the equivalent of [menu_with_enabled](Self::menu_with_enabled) /// with `enabled` set to `true`. #[doc(alias = "BeginMenu")] - pub fn menu(&self, label: &ImStr, f: F) { + pub fn menu(&self, label: impl AsRef, f: F) { self.menu_with_enabled(label, true, f); } @@ -100,7 +102,7 @@ impl<'ui> Ui<'ui> { /// /// Note: the closure is not called if the menu is not visible. #[doc(alias = "BeginMenu")] - pub fn menu_with_enabled(&self, label: &ImStr, enabled: bool, f: F) { + pub fn menu_with_enabled(&self, label: impl AsRef, enabled: bool, f: F) { if let Some(_menu) = self.begin_menu_with_enabled(label, enabled) { f(); } @@ -110,16 +112,16 @@ impl<'ui> Ui<'ui> { /// Builder for a menu item. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct MenuItem<'a> { - label: &'a ImStr, - shortcut: Option<&'a ImStr>, +pub struct MenuItem<'a, T> { + label: T, + shortcut: Option<&'a str>, selected: bool, enabled: bool, } -impl<'a> MenuItem<'a> { +impl<'a, T: 'a + AsRef> MenuItem<'a, T> { /// Construct a new menu item builder. - pub fn new(label: &ImStr) -> MenuItem { + pub fn new(label: T) -> Self { MenuItem { label, shortcut: None, @@ -131,7 +133,7 @@ impl<'a> MenuItem<'a> { /// /// Shortcuts are displayed for convenience only and are not automatically handled. #[inline] - pub fn shortcut(mut self, shortcut: &'a ImStr) -> Self { + pub fn shortcut(mut self, shortcut: &'a str) -> Self { self.shortcut = Some(shortcut); self } @@ -155,20 +157,14 @@ impl<'a> MenuItem<'a> { /// /// Returns true if the menu item is activated. #[doc(alias = "MenuItemBool")] - pub fn build(self, _: &Ui) -> bool { + pub fn build(self, ui: &Ui) -> bool { unsafe { - sys::igMenuItemBool( - self.label.as_ptr(), - self.shortcut.map(ImStr::as_ptr).unwrap_or(ptr::null()), - self.selected, - self.enabled, - ) + let (label, shortcut) = ui.scratch_txt_with_opt(self.label, self.shortcut); + sys::igMenuItemBool(label, shortcut, self.selected, self.enabled) } } -} -/// # Convenience functions -impl<'a> MenuItem<'a> { + #[doc(alias = "MenuItemBool")] /// Builds the menu item using a mutable reference to selected state. pub fn build_with_ref(self, ui: &Ui, selected: &mut bool) -> bool { if self.selected(*selected).build(ui) { From 5143968429a680f4e6bc5fbb313742861818f142 Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Wed, 8 Sep 2021 09:05:30 -0700 Subject: [PATCH 06/21] updating more widgets. Wow this is boring! --- imgui-examples/examples/progress_bar.rs | 4 +- imgui-examples/examples/slider.rs | 2 +- imgui-examples/examples/test_window_impl.rs | 2 +- imgui/src/lib.rs | 8 ++ imgui/src/widget/progress_bar.rs | 36 +++--- imgui/src/widget/selectable.rs | 13 +- imgui/src/widget/slider.rs | 112 +++++++++--------- imgui/src/widget/tab.rs | 125 +++++++++++++------- imgui/src/widget/text.rs | 6 +- 9 files changed, 181 insertions(+), 127 deletions(-) diff --git a/imgui-examples/examples/progress_bar.rs b/imgui-examples/examples/progress_bar.rs index b5bd9db..65cbcd5 100644 --- a/imgui-examples/examples/progress_bar.rs +++ b/imgui-examples/examples/progress_bar.rs @@ -19,9 +19,7 @@ fn main() { ui.separator(); ui.text("This progress bar uses overlay text:"); - ProgressBar::new(0.8) - .overlay_text(im_str!("Lorem ipsum")) - .build(ui); + ProgressBar::new_with_overlay(0.8, "Lorem ipsum").build(ui); }); }); } diff --git a/imgui-examples/examples/slider.rs b/imgui-examples/examples/slider.rs index 2b3698e..00c1428 100644 --- a/imgui-examples/examples/slider.rs +++ b/imgui-examples/examples/slider.rs @@ -56,7 +56,7 @@ fn example_1(ui: &Ui, state: &mut State) { ui.separator(); ui.text("Value formatting can be customized with a C-style printf string:"); Slider::new(im_str!("f64 value with custom formatting"), -999_999_999.0, 999_999_999.0) - .display_format(im_str!("%09.0f")) + .display_format("%09.0f") .build(ui, &mut state.f64_formatted); ui.separator(); diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 5e88788..0398b84 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -449,7 +449,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.spacing(); Slider::new(im_str!("Wrap width"), -20.0, 600.0) - .display_format(im_str!("%.0f")) + .display_format("%.0f") .build(ui, &mut state.wrap_width); ui.text(im_str!("Test paragraph 1:")); diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 82a2882..563883a 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -137,6 +137,14 @@ impl<'ui> Ui<'ui> { } } + /// Internal method to push an option text to our scratch buffer. + fn scratch_txt_opt(&self, txt: Option>) -> *const sys::cty::c_char { + match txt { + Some(v) => self.scratch_txt(v), + None => std::ptr::null(), + } + } + fn scratch_txt_two( &self, txt_0: impl AsRef, diff --git a/imgui/src/widget/progress_bar.rs b/imgui/src/widget/progress_bar.rs index 255399b..5a806af 100644 --- a/imgui/src/widget/progress_bar.rs +++ b/imgui/src/widget/progress_bar.rs @@ -1,7 +1,5 @@ -use std::ptr; - -use crate::string::ImStr; use crate::sys; +// use crate::ImStr; use crate::Ui; /// Builder for a progress bar widget. @@ -19,13 +17,13 @@ use crate::Ui; /// ``` #[derive(Copy, Clone, Debug)] #[must_use] -pub struct ProgressBar<'a> { +pub struct ProgressBar { fraction: f32, size: [f32; 2], - overlay_text: Option<&'a ImStr>, + overlay_text: Option, } -impl<'a> ProgressBar<'a> { +impl ProgressBar<&'static str> { /// Creates a progress bar with a given fraction showing /// the progress (0.0 = 0%, 1.0 = 100%). /// @@ -33,19 +31,29 @@ impl<'a> ProgressBar<'a> { /// custom size is specified. #[inline] #[doc(alias = "ProgressBar")] - pub const fn new(fraction: f32) -> ProgressBar<'a> { + pub fn new(fraction: f32) -> Self { ProgressBar { fraction, size: [-1.0, 0.0], overlay_text: None, } } +} - /// Sets an optional text that will be drawn over the progress bar. +impl> ProgressBar { + /// Creates a progress bar with a given fraction showing + /// the progress (0.0 = 0%, 1.0 = 100%). + /// + /// The progress bar will be automatically sized to fill the entire width of the window if no + /// custom size is specified. #[inline] - pub const fn overlay_text(mut self, overlay_text: &'a ImStr) -> ProgressBar { - self.overlay_text = Some(overlay_text); - self + #[doc(alias = "ProgressBar")] + pub fn new_with_overlay(fraction: f32, overlay_text: T) -> Self { + ProgressBar { + fraction, + size: [-1.0, 0.0], + overlay_text: Some(overlay_text), + } } /// Sets the size of the progress bar. @@ -53,18 +61,18 @@ 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 const fn size(mut self, size: [f32; 2]) -> Self { + pub fn size(mut self, size: [f32; 2]) -> Self { self.size = size; self } /// Builds the progress bar - pub fn build(self, _: &Ui) { + pub fn build(self, ui: &Ui) { unsafe { sys::igProgressBar( self.fraction, self.size.into(), - self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()), + ui.scratch_txt_opt(self.overlay_text), ); } } diff --git a/imgui/src/widget/selectable.rs b/imgui/src/widget/selectable.rs index 83fd455..e266995 100644 --- a/imgui/src/widget/selectable.rs +++ b/imgui/src/widget/selectable.rs @@ -23,20 +23,20 @@ bitflags!( /// Builder for a selectable widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct Selectable<'a> { - label: &'a str, +pub struct Selectable { + label: T, selected: bool, flags: SelectableFlags, size: [f32; 2], } -impl<'a> Selectable<'a> { +impl> Selectable { /// Constructs a new selectable builder. #[inline] #[doc(alias = "Selectable")] - pub fn new(label: &'a impl AsRef) -> Selectable<'a> { + pub fn new(label: T) -> Self { Selectable { - label: label.as_ref(), + label, selected: false, flags: SelectableFlags::empty(), size: [0.0, 0.0], @@ -117,10 +117,7 @@ impl<'a> Selectable<'a> { ) } } -} -/// # Convenience functions -impl<'a> Selectable<'a> { /// Builds the selectable using a mutable reference to selected state. pub fn build_with_ref(self, ui: &Ui, selected: &mut bool) -> bool { if self.selected(*selected).build(ui) { diff --git a/imgui/src/widget/slider.rs b/imgui/src/widget/slider.rs index 531b155..f08e026 100644 --- a/imgui/src/widget/slider.rs +++ b/imgui/src/widget/slider.rs @@ -1,9 +1,7 @@ use bitflags::bitflags; use std::os::raw::c_void; -use std::ptr; use crate::internal::DataTypeKind; -use crate::string::ImStr; use crate::sys; use crate::Ui; @@ -27,18 +25,18 @@ bitflags!( /// Builder for a slider widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct Slider<'a, T: DataTypeKind> { - label: &'a ImStr, - min: T, - max: T, - display_format: Option<&'a ImStr>, +pub struct Slider<'a, T, K: DataTypeKind> { + label: T, + min: K, + max: K, + display_format: Option<&'a str>, flags: SliderFlags, } -impl<'a, T: DataTypeKind> Slider<'a, T> { +impl<'a, T: AsRef, K: DataTypeKind> Slider<'a, T, K> { /// Constructs a new slider builder with the given range. #[doc(alias = "SliderScalar", alias = "SliderScalarN")] - pub fn new(label: &ImStr, min: T, max: T) -> Slider { + pub fn new(label: T, min: K, max: K) -> Self { Slider { label, min, @@ -61,14 +59,14 @@ impl<'a, T: DataTypeKind> Slider<'a, T> { /// It is safe, though up to C++ Dear ImGui, on how to handle when /// `min > max`. #[inline] - pub fn range(mut self, min: T, max: T) -> Self { + pub fn range(mut self, min: K, max: K) -> Self { self.min = min; self.max = max; self } /// Sets the display format using *a C-style printf string* #[inline] - pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + pub fn display_format(mut self, display_format: &'a str) -> Self { self.display_format = Some(display_format); self } @@ -81,17 +79,17 @@ impl<'a, T: DataTypeKind> Slider<'a, T> { /// Builds a slider that is bound to the given value. /// /// Returns true if the slider value was changed. - pub fn build(self, _: &Ui, value: &mut T) -> bool { + pub fn build(self, ui: &Ui, value: &mut K) -> bool { unsafe { + let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format); + sys::igSliderScalar( - self.label.as_ptr(), - T::KIND as i32, - value as *mut T as *mut c_void, - &self.min as *const T as *const c_void, - &self.max as *const T as *const c_void, - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + label, + K::KIND as i32, + value as *mut K as *mut c_void, + &self.min as *const K as *const c_void, + &self.max as *const K as *const c_void, + display_format, self.flags.bits() as i32, ) } @@ -99,18 +97,18 @@ impl<'a, T: DataTypeKind> Slider<'a, T> { /// Builds a horizontal array of multiple sliders attached to the given slice. /// /// Returns true if any slider value was changed. - pub fn build_array(self, _: &Ui, values: &mut [T]) -> bool { + pub fn build_array(self, ui: &Ui, values: &mut [K]) -> bool { unsafe { + let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format); + sys::igSliderScalarN( - self.label.as_ptr(), - T::KIND as i32, + label, + K::KIND as i32, values.as_mut_ptr() as *mut c_void, values.len() as i32, - &self.min as *const T as *const c_void, - &self.max as *const T as *const c_void, - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + &self.min as *const K as *const c_void, + &self.max as *const K as *const c_void, + display_format, self.flags.bits() as i32, ) } @@ -120,16 +118,16 @@ impl<'a, T: DataTypeKind> Slider<'a, T> { /// Builder for a vertical slider widget. #[derive(Clone, Debug)] #[must_use] -pub struct VerticalSlider<'a, T: DataTypeKind + Copy> { - label: &'a ImStr, +pub struct VerticalSlider<'a, T, K: DataTypeKind> { + label: T, size: [f32; 2], - min: T, - max: T, - display_format: Option<&'a ImStr>, + min: K, + max: K, + display_format: Option<&'a str>, flags: SliderFlags, } -impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { +impl<'a, T: AsRef, K: DataTypeKind> VerticalSlider<'a, T, K> { /// Constructs a new vertical slider builder with the given size and range. /// /// ```rust @@ -143,7 +141,7 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { /// It is safe, though up to C++ Dear ImGui, on how to handle when /// `min > max`. #[doc(alias = "VSliderScalar")] - pub fn new(label: &ImStr, size: [f32; 2], min: T, max: T) -> VerticalSlider { + pub fn new(label: T, size: [f32; 2], min: K, max: K) -> Self { VerticalSlider { label, size, @@ -167,14 +165,14 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { /// It is safe, though up to C++ Dear ImGui, on how to handle when /// `min > max`. #[inline] - pub fn range(mut self, min: T, max: T) -> Self { + pub fn range(mut self, min: K, max: K) -> Self { self.min = min; self.max = max; self } /// Sets the display format using *a C-style printf string* #[inline] - pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + pub fn display_format(mut self, display_format: &'a str) -> Self { self.display_format = Some(display_format); self } @@ -187,18 +185,18 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { /// Builds a vertical slider that is bound to the given value. /// /// Returns true if the slider value was changed. - pub fn build(self, _: &Ui, value: &mut T) -> bool { + pub fn build(self, ui: &Ui, value: &mut K) -> bool { unsafe { + let (label, display_format) = ui.scratch_txt_with_opt(self.label, self.display_format); + sys::igVSliderScalar( - self.label.as_ptr(), + label, self.size.into(), - T::KIND as i32, - value as *mut T as *mut c_void, - &self.min as *const T as *const c_void, - &self.max as *const T as *const c_void, - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + K::KIND as i32, + value as *mut K as *mut c_void, + &self.min as *const K as *const c_void, + &self.max as *const K as *const c_void, + display_format, self.flags.bits() as i32, ) } @@ -208,24 +206,24 @@ impl<'a, T: DataTypeKind> VerticalSlider<'a, T> { /// Builder for an angle slider widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct AngleSlider<'a> { - label: &'a ImStr, +pub struct AngleSlider<'a, T> { + label: T, min_degrees: f32, max_degrees: f32, - display_format: &'a ImStr, + display_format: &'a str, flags: SliderFlags, } -impl<'a> AngleSlider<'a> { +impl<'a, T: AsRef> AngleSlider<'a, T> { /// Constructs a new angle slider builder, where its minimum defaults to -360.0 and /// maximum defaults to 360.0 #[doc(alias = "SliderAngle")] - pub fn new(label: &ImStr) -> AngleSlider { + pub fn new(label: T) -> Self { AngleSlider { label, min_degrees: -360.0, max_degrees: 360.0, - display_format: im_str!("%.0f deg"), + display_format: "%.0f deg", flags: SliderFlags::empty(), } } @@ -260,7 +258,7 @@ impl<'a> AngleSlider<'a> { } /// Sets the display format using *a C-style printf string* #[inline] - pub fn display_format(mut self, display_format: &'a ImStr) -> Self { + pub fn display_format(mut self, display_format: &'a str) -> Self { self.display_format = display_format; self } @@ -273,14 +271,16 @@ impl<'a> AngleSlider<'a> { /// Builds an angle slider that is bound to the given value (in radians). /// /// Returns true if the slider value was changed. - pub fn build(self, _: &Ui, value_rad: &mut f32) -> bool { + pub fn build(self, ui: &Ui, value_rad: &mut f32) -> bool { unsafe { + let (label, display_format) = ui.scratch_txt_two(self.label, self.display_format); + sys::igSliderAngle( - self.label.as_ptr(), + label, value_rad as *mut _, self.min_degrees, self.max_degrees, - self.display_format.as_ptr(), + display_format, self.flags.bits() as i32, ) } diff --git a/imgui/src/widget/tab.rs b/imgui/src/widget/tab.rs index 20f6ed2..5719443 100644 --- a/imgui/src/widget/tab.rs +++ b/imgui/src/widget/tab.rs @@ -1,5 +1,3 @@ -//! Safe wrapper around imgui-sys for tab menu. -//! //! # Examples // //! ```no_run @@ -19,7 +17,6 @@ //! ``` //! //! See `test_window_impl.rs` for a more complicated example. -use crate::string::ImStr; use crate::sys; use crate::Ui; use bitflags::bitflags; @@ -56,15 +53,15 @@ bitflags! { } /// Builder for a tab bar. -pub struct TabBar<'a> { - id: &'a ImStr, +pub struct TabBar { + id: T, flags: TabBarFlags, } -impl<'a> TabBar<'a> { +impl> TabBar { #[inline] #[doc(alias = "BeginTabBar")] - pub const fn new(id: &'a ImStr) -> Self { + pub fn new(id: T) -> Self { Self { id, flags: TabBarFlags::empty(), @@ -90,23 +87,15 @@ impl<'a> TabBar<'a> { } #[must_use] - pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option> { - let should_render = - unsafe { sys::igBeginTabBar(self.id.as_ptr(), self.flags.bits() as i32) }; - - if should_render { - Some(TabBarToken::new(ui)) - } else { - unsafe { sys::igEndTabBar() }; - None - } + pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option> { + ui.tab_bar_with_flags(self.id, self.flags) } /// Creates a tab bar and runs a closure to construct the contents. /// Returns the result of the closure, if it is called. /// /// Note: the closure is not called if no tabbar content is visible - pub fn build T>(self, ui: &Ui<'_>, f: F) -> Option { + pub fn build R>(self, ui: &Ui<'_>, f: F) -> Option { self.begin(ui).map(|_tab| f()) } } @@ -120,23 +109,23 @@ create_token!( drop { sys::igEndTabBar() } ); -pub struct TabItem<'a> { - name: &'a ImStr, +pub struct TabItem<'a, T> { + label: T, opened: Option<&'a mut bool>, flags: TabItemFlags, } -impl<'a> TabItem<'a> { +impl<'a, T: AsRef> TabItem<'a, T> { #[doc(alias = "BeginTabItem")] - pub fn new(name: &'a ImStr) -> Self { + pub fn new(name: T) -> Self { Self { - name, + label: name, opened: None, flags: TabItemFlags::empty(), } } - /// Will open or close the tab.\ + /// Will open or close the tab. /// /// True to display the tab. Tab item is visible by default. #[inline] @@ -155,29 +144,15 @@ impl<'a> TabItem<'a> { } #[must_use] - pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option> { - let should_render = unsafe { - sys::igBeginTabItem( - self.name.as_ptr(), - self.opened - .map(|x| x as *mut bool) - .unwrap_or(ptr::null_mut()), - self.flags.bits() as i32, - ) - }; - - if should_render { - Some(TabItemToken::new(ui)) - } else { - None - } + pub fn begin<'ui>(self, ui: &'ui Ui<'_>) -> Option> { + ui.tab_item_with_flags(self.label, self.opened, self.flags) } /// Creates a tab item and runs a closure to construct the contents. /// Returns the result of the closure, if it is called. /// /// Note: the closure is not called if the tab item is not selected - pub fn build T>(self, ui: &Ui<'_>, f: F) -> Option { + pub fn build R>(self, ui: &Ui<'_>, f: F) -> Option { self.begin(ui).map(|_tab| f()) } } @@ -190,3 +165,71 @@ create_token!( /// Ends a tab bar item. drop { sys::igEndTabItem() } ); + +impl Ui<'_> { + /// Creates a tab bar and returns a tab bar token, allowing you to append + /// Tab items afterwards. This passes no flags. To pass flags explicitly, + /// use [tab_bar_with_flags]. + pub fn tab_bar(&self, id: impl AsRef) -> Option> { + self.tab_bar_with_flags(id, TabBarFlags::empty()) + } + // + /// Creates a tab bar and returns a tab bar token, allowing you to append + /// Tab items afterwards. + pub fn tab_bar_with_flags( + &self, + id: impl AsRef, + flags: TabBarFlags, + ) -> Option> { + let should_render = + unsafe { sys::igBeginTabBar(self.scratch_txt(id), flags.bits() as i32) }; + + if should_render { + Some(TabBarToken::new(self)) + } else { + unsafe { sys::igEndTabBar() }; + None + } + } + + /// Creates a new tab item and returns a token if its contents are visible. + /// + /// By default, this doesn't pass an opened bool nor any flags. See [tab_item_with_opened] + /// and `[tab_item_with_flags]` for more. + pub fn tab_item(&self, label: impl AsRef) -> Option> { + self.tab_item_with_flags(label, None, TabItemFlags::empty()) + } + + /// Creates a new tab item and returns a token if its contents are visible. + /// + /// By default, this doesn't pass any flags. See `[tab_item_with_flags]` for more. + pub fn tab_item_with_opened( + &self, + label: impl AsRef, + opened: &mut bool, + ) -> Option> { + self.tab_item_with_flags(label, Some(opened), TabItemFlags::empty()) + } + + /// Creates a new tab item and returns a token if its contents are visible. + pub fn tab_item_with_flags( + &self, + label: impl AsRef, + opened: Option<&mut bool>, + flags: TabItemFlags, + ) -> Option> { + let should_render = unsafe { + sys::igBeginTabItem( + self.scratch_txt(label), + opened.map(|x| x as *mut bool).unwrap_or(ptr::null_mut()), + flags.bits() as i32, + ) + }; + + if should_render { + Some(TabItemToken::new(self)) + } else { + None + } + } +} diff --git a/imgui/src/widget/text.rs b/imgui/src/widget/text.rs index 65f3849..7273ec4 100644 --- a/imgui/src/widget/text.rs +++ b/imgui/src/widget/text.rs @@ -1,6 +1,6 @@ use std::os::raw::c_char; -use crate::string::ImStr; +// use crate::string::ImStr; use crate::style::StyleColor; use crate::Ui; @@ -49,7 +49,7 @@ impl<'ui> Ui<'ui> { } /// Renders text with a little bullet aligned to the typical tree node #[doc(alias = "BulletText")] - pub fn bullet_text(&self, text: &ImStr) { - unsafe { sys::igBulletText(fmt_ptr(), text.as_ptr()) } + pub fn bullet_text(&self, text: impl AsRef) { + unsafe { sys::igBulletText(fmt_ptr(), self.scratch_txt(text)) } } } From fc49af214a296a106693cc7a653e035aa0310731 Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Fri, 10 Sep 2021 18:19:46 -0700 Subject: [PATCH 07/21] removed imstr from the tree --- imgui/src/widget/tree.rs | 122 ++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/imgui/src/widget/tree.rs b/imgui/src/widget/tree.rs index 1c42ebc..f764188 100644 --- a/imgui/src/widget/tree.rs +++ b/imgui/src/widget/tree.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use std::os::raw::{c_char, c_void}; -use crate::string::ImStr; +// use crate::string::ImStr; use crate::sys; use crate::{Condition, Ui}; @@ -61,27 +61,26 @@ fn fmt_ptr() -> *const c_char { /// Unique ID used by tree nodes #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum TreeNodeId<'a> { - Str(&'a ImStr), +pub enum TreeNodeId { + Str(T), Ptr(*const c_void), } -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 for TreeNodeId { + fn from(s: T) -> Self { + TreeNodeId::Str(s) } } -impl From<*const T> for TreeNodeId<'static> { - #[inline] +// this is a bit wonky here using the T param... +impl From<*const T> for TreeNodeId { fn from(p: *const T) -> Self { TreeNodeId::Ptr(p as *const c_void) } } -impl From<*mut T> for TreeNodeId<'static> { - #[inline] +// this is a bit wonky here using the T param... +impl From<*mut T> for TreeNodeId { fn from(p: *mut T) -> Self { TreeNodeId::Ptr(p as *const T as *const c_void) } @@ -90,17 +89,17 @@ impl From<*mut T> for TreeNodeId<'static> { /// Builder for a tree node widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct TreeNode<'a> { - id: TreeNodeId<'a>, - label: Option<&'a ImStr>, +pub struct TreeNode { + id: TreeNodeId, + label: Option, opened: bool, opened_cond: Condition, flags: TreeNodeFlags, } -impl<'a> TreeNode<'a> { +impl> TreeNode { /// Constructs a new tree node builder - pub fn new>>(id: I) -> TreeNode<'a> { + pub fn new>>(id: I) -> TreeNode { TreeNode { id: id.into(), label: None, @@ -109,125 +108,133 @@ impl<'a> TreeNode<'a> { flags: TreeNodeFlags::empty(), } } +} + +impl, L: AsRef> TreeNode { /// Sets the tree node label - #[inline] - pub fn label(mut self, label: &'a ImStr) -> Self { - self.label = Some(label); - self + pub fn label>, L2: AsRef>(self, label: L2) -> TreeNode { + TreeNode { + label: Some(label), + id: self.id, + opened: self.opened, + opened_cond: self.opened_cond, + flags: self.flags, + } } + /// Sets the opened state of the tree node, which is applied based on the given condition value - #[inline] pub fn opened(mut self, opened: bool, cond: Condition) -> Self { self.opened = opened; self.opened_cond = cond; self } + /// Replaces all current settings with the given flags. - #[inline] pub fn flags(mut self, flags: TreeNodeFlags) -> Self { self.flags = flags; self } + /// Enables/disables drawing the tree node in selected state. /// /// Disabled by default. - #[inline] pub fn selected(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::SELECTED, value); self } + /// Enables/disables full-colored frame. /// /// Disabled by default. - #[inline] pub fn framed(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::FRAMED, value); self } + /// Enables/disables allowing the tree node to overlap subsequent widgets. /// /// Disabled by default. - #[inline] pub fn allow_item_overlap(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::ALLOW_ITEM_OVERLAP, value); self } + /// Enables/disables automatic tree push when the tree node is open (= adds extra indentation /// and pushes to the ID stack). /// /// Enabled by default. - #[inline] pub fn tree_push_on_open(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN, !value); self } + /// Enables/disables automatic opening of the tree node when logging is active. /// /// By default, logging will automatically open all tree nodes. /// /// Enabled by default. - #[inline] pub fn auto_open_on_log(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::NO_AUTO_OPEN_ON_LOG, !value); self } + /// Sets the default open state for the tree node. /// /// Tree nodes are closed by default. - #[inline] pub fn default_open(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::DEFAULT_OPEN, value); self } + /// Only open when the tree node is double-clicked. /// /// Disabled by default. - #[inline] pub fn open_on_double_click(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::OPEN_ON_DOUBLE_CLICK, value); self } + /// Only open when clicking the arrow part of the tree node. /// /// Disabled by default. - #[inline] pub fn open_on_arrow(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::OPEN_ON_ARROW, value); self } + /// Enable/disables leaf mode (no collapsing, no arrow). /// /// Disabled by default. - #[inline] pub fn leaf(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::LEAF, value); self } + /// Display a bullet instead of arrow. /// /// Disabled by default. - #[inline] pub fn bullet(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::BULLET, value); self } + /// Use `frame_padding` to vertically align text baseline to regular widget height. /// /// Disabled by default. - #[inline] pub fn frame_padding(mut self, value: bool) -> Self { self.flags.set(TreeNodeFlags::FRAME_PADDING, value); self } + /// Left direction may move to this tree node from any of its child. /// /// Disabled by default. - #[inline] pub fn nav_left_jumps_back_here(mut self, value: bool) -> Self { self.flags .set(TreeNodeFlags::NAV_LEFT_JUMPS_BACK_HERE, value); self } + /// Pushes a tree node and starts appending to it. /// /// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been @@ -240,17 +247,25 @@ impl<'a> TreeNode<'a> { sys::igSetNextItemOpen(self.opened, self.opened_cond as i32); } match self.id { - TreeNodeId::Str(id) => sys::igTreeNodeExStrStr( - id.as_ptr(), - self.flags.bits() as i32, - fmt_ptr(), - self.label.unwrap_or(id).as_ptr(), - ), + TreeNodeId::Str(id) => { + let (id, label) = match self.label { + Some(label) => ui.scratch_txt_two(id, label), + None => { + let v = ui.scratch_txt(id); + (v, v) + } + }; + + sys::igTreeNodeExStrStr(id, self.flags.bits() as i32, fmt_ptr(), label) + } TreeNodeId::Ptr(id) => sys::igTreeNodeExPtr( id, self.flags.bits() as i32, fmt_ptr(), - self.label.unwrap_or_default().as_ptr(), + match self.label { + Some(v) => ui.scratch_txt(v), + None => ui.scratch_txt(""), + }, ), } }; @@ -267,7 +282,7 @@ impl<'a> TreeNode<'a> { /// Returns the result of the closure, if it is called. /// /// Note: the closure is not called if the tree node is not open. - pub fn build T>(self, ui: &Ui<'_>, f: F) -> Option { + pub fn build R>(self, ui: &Ui<'_>, f: F) -> Option { self.push(ui).map(|_node| f()) } } @@ -310,15 +325,15 @@ impl Drop for TreeNodeToken<'_> { /// Builder for a collapsing header widget #[derive(Copy, Clone, Debug)] #[must_use] -pub struct CollapsingHeader<'a> { - label: &'a ImStr, +pub struct CollapsingHeader { + label: T, flags: TreeNodeFlags, } -impl<'a> CollapsingHeader<'a> { +impl> CollapsingHeader { /// Constructs a new collapsing header builder #[doc(alias = "CollapsingHeader")] - pub fn new(label: &ImStr) -> CollapsingHeader { + pub fn new(label: T) -> CollapsingHeader { CollapsingHeader { label, flags: TreeNodeFlags::empty(), @@ -413,9 +428,12 @@ impl<'a> CollapsingHeader<'a> { /// Returns true if the collapsing header is open and content should be rendered. #[must_use] #[inline] - pub fn build(self, _: &Ui) -> bool { + pub fn build(self, ui: &Ui) -> bool { unsafe { - sys::igCollapsingHeaderTreeNodeFlags(self.label.as_ptr(), self.flags.bits() as i32) + sys::igCollapsingHeaderTreeNodeFlags( + ui.scratch_txt(self.label), + self.flags.bits() as i32, + ) } } /// Builds the collapsing header, and adds an additional close button that changes the value of @@ -424,10 +442,10 @@ impl<'a> CollapsingHeader<'a> { /// Returns true if the collapsing header is open and content should be rendered. #[must_use] #[inline] - pub fn build_with_close_button(self, _: &Ui, opened: &mut bool) -> bool { + pub fn build_with_close_button(self, ui: &Ui, opened: &mut bool) -> bool { unsafe { sys::igCollapsingHeaderBoolPtr( - self.label.as_ptr(), + ui.scratch_txt(self.label), opened as *mut bool, self.flags.bits() as i32, ) @@ -438,7 +456,7 @@ impl<'a> CollapsingHeader<'a> { impl Ui<'_> { /// Constructs a new collapsing header #[doc(alias = "CollapsingHeader")] - pub fn collapsing_header(&self, label: &ImStr, flags: TreeNodeFlags) -> bool { + pub fn collapsing_header(&self, label: impl AsRef, flags: TreeNodeFlags) -> bool { CollapsingHeader::new(label).flags(flags).build(self) } @@ -446,7 +464,7 @@ impl Ui<'_> { #[doc(alias = "CollapsingHeader")] pub fn collapsing_header_with_close_button( &self, - label: &ImStr, + label: impl AsRef, flags: TreeNodeFlags, opened: &mut bool, ) -> bool { From e0125f4c06e2834c9b5f5abaf9e9b878f1f84891 Mon Sep 17 00:00:00 2001 From: Jack Spira Date: Fri, 10 Sep 2021 18:40:47 -0700 Subject: [PATCH 08/21] removed imstr and imstring from clipboard support --- imgui-examples/examples/support/clipboard.rs | 11 ++-- imgui-examples/examples/support/mod.rs | 2 +- imgui/src/clipboard.rs | 54 +++++++++++--------- imgui/src/context.rs | 6 +-- imgui/src/window/mod.rs | 13 +++-- 5 files changed, 47 insertions(+), 39 deletions(-) diff --git a/imgui-examples/examples/support/clipboard.rs b/imgui-examples/examples/support/clipboard.rs index d970b29..297a696 100644 --- a/imgui-examples/examples/support/clipboard.rs +++ b/imgui-examples/examples/support/clipboard.rs @@ -1,5 +1,5 @@ use clipboard::{ClipboardContext, ClipboardProvider}; -use imgui::{ClipboardBackend, ImStr, ImString}; +use imgui::ClipboardBackend; pub struct ClipboardSupport(ClipboardContext); @@ -8,10 +8,11 @@ pub fn init() -> Option { } impl ClipboardBackend for ClipboardSupport { - fn get(&mut self) -> Option { - self.0.get_contents().ok().map(|text| text.into()) + fn get(&mut self) -> Option { + self.0.get_contents().ok() } - fn set(&mut self, text: &ImStr) { - let _ = self.0.set_contents(text.to_str().to_owned()); + fn set(&mut self, text: &str) { + // ignore errors? + let _ = self.0.set_contents(text.to_owned()); } } diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index 0a2ea36..2a5c9d5 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -37,7 +37,7 @@ pub fn init(title: &str) -> System { imgui.set_ini_filename(None); if let Some(backend) = clipboard::init() { - imgui.set_clipboard_backend(Box::new(backend)); + imgui.set_clipboard_backend(backend); } else { eprintln!("Failed to initialize clipboard"); } diff --git a/imgui/src/clipboard.rs b/imgui/src/clipboard.rs index e6ff182..2c9ac9c 100644 --- a/imgui/src/clipboard.rs +++ b/imgui/src/clipboard.rs @@ -1,45 +1,46 @@ +use std::ffi::{CStr, CString}; use std::fmt; use std::os::raw::{c_char, c_void}; use std::panic::catch_unwind; use std::process; use std::ptr; -use crate::string::{ImStr, ImString}; +// use crate::string::{ImStr, ImString}; use crate::Ui; /// Trait for clipboard backends -pub trait ClipboardBackend { +pub trait ClipboardBackend: 'static { /// Returns the current clipboard contents as an owned imgui-rs string, or None if the /// clipboard is empty or inaccessible - fn get(&mut self) -> Option; + fn get(&mut self) -> Option; /// Sets the clipboard contents to the given imgui-rs string slice. - fn set(&mut self, value: &ImStr); + fn set(&mut self, value: &str); } pub(crate) struct ClipboardContext { backend: Box, // this is needed to keep ownership of the value when the raw C callback is called - last_value: ImString, + last_value: CString, } impl ClipboardContext { - #[inline] - pub fn new(backend: Box) -> ClipboardContext { + /// Creates a new [ClipboardContext]. This function previously took a `Box`, but now + /// is generic over the T it takes and boxes itself (which should be less strange). + pub fn new(backend: T) -> ClipboardContext { ClipboardContext { - backend, - last_value: ImString::default(), + backend: Box::new(backend) as Box, + last_value: CString::default(), } } } impl fmt::Debug for ClipboardContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "ClipboardContext({:?}, {:?})", - &(*self.backend) as *const _, - self.last_value - ) + f.debug_struct("ClipboardContext") + // beautiful code, no? + .field("backend", &(&(*self.backend) as *const _)) + .field("last_value", &self.last_value) + .finish() } } @@ -48,7 +49,7 @@ pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *c let ctx = &mut *(user_data as *mut ClipboardContext); match ctx.backend.get() { Some(text) => { - ctx.last_value = text; + ctx.last_value = CString::new(text).unwrap(); ctx.last_value.as_ptr() } None => ptr::null(), @@ -63,8 +64,8 @@ pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *c pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: *const c_char) { let result = catch_unwind(|| { let ctx = &mut *(user_data as *mut ClipboardContext); - let text = ImStr::from_ptr_unchecked(text); - ctx.backend.set(text); + let text = CStr::from_ptr(text).to_owned(); + ctx.backend.set(text.to_str().unwrap()); }); result.unwrap_or_else(|_| { eprintln!("Clipboard setter panicked"); @@ -77,7 +78,7 @@ pub(crate) unsafe extern "C" fn set_clipboard_text(user_data: *mut c_void, text: impl<'ui> Ui<'ui> { /// Returns the current clipboard contents as text, or None if the clipboard is empty or cannot /// be accessed - pub fn clipboard_text(&self) -> Option { + pub fn clipboard_text(&self) -> Option { let io = self.io(); io.get_clipboard_text_fn.and_then(|get_clipboard_text_fn| { // Bypass FFI if we end up calling our own function anyway @@ -90,25 +91,32 @@ impl<'ui> Ui<'ui> { if text_ptr.is_null() || *text_ptr == b'\0' as c_char { None } else { - Some(ImStr::from_ptr_unchecked(text_ptr).to_owned()) + Some( + CStr::from_ptr(text_ptr) + .to_owned() + .to_str() + .ok()? + .to_owned(), + ) } } } }) } + /// Sets the clipboard contents. /// /// Does nothing if the clipboard cannot be accessed. - pub fn set_clipboard_text(&self, text: &ImStr) { + pub fn set_clipboard_text(&self, text: impl AsRef) { let io = self.io(); if let Some(set_clipboard_text_fn) = io.set_clipboard_text_fn { // Bypass FFI if we end up calling our own function anyway if set_clipboard_text_fn == set_clipboard_text { let ctx = unsafe { &mut *(io.clipboard_user_data as *mut ClipboardContext) }; - ctx.backend.set(text); + ctx.backend.set(text.as_ref()); } else { unsafe { - set_clipboard_text_fn(io.clipboard_user_data, text.as_ptr()); + set_clipboard_text_fn(io.clipboard_user_data, self.scratch_txt(text)); } } } diff --git a/imgui/src/context.rs b/imgui/src/context.rs index eba5448..4f190d4 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -56,7 +56,7 @@ pub struct Context { log_filename: Option, platform_name: Option, renderer_name: Option, - clipboard_ctx: Option>, + clipboard_ctx: Option, } // This mutex needs to be used to guard all public functions that can affect the underlying @@ -197,9 +197,9 @@ impl Context { buf.push_str(&data.to_string_lossy()); } /// Sets the clipboard backend used for clipboard operations - pub fn set_clipboard_backend(&mut self, backend: Box) { + pub fn set_clipboard_backend(&mut self, backend: T) { use std::borrow::BorrowMut; - let mut clipboard_ctx = Box::new(ClipboardContext::new(backend)); + let mut clipboard_ctx = ClipboardContext::new(backend); let io = self.io_mut(); io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text); io.get_clipboard_text_fn = Some(crate::clipboard::get_clipboard_text); diff --git a/imgui/src/window/mod.rs b/imgui/src/window/mod.rs index f5caafb..04161cd 100644 --- a/imgui/src/window/mod.rs +++ b/imgui/src/window/mod.rs @@ -2,7 +2,6 @@ use bitflags::bitflags; use std::f32; use std::ptr; -use crate::string::ImStr; use crate::sys; use crate::{Condition, Ui}; @@ -161,8 +160,8 @@ impl<'ui> Ui<'ui> { /// Builder for a window #[derive(Debug)] #[must_use] -pub struct Window<'a> { - name: &'a ImStr, +pub struct Window<'a, T> { + name: T, opened: Option<&'a mut bool>, flags: WindowFlags, pos: [f32; 2], @@ -178,9 +177,9 @@ pub struct Window<'a> { bg_alpha: f32, } -impl<'a> Window<'a> { +impl<'a, T: AsRef> Window<'a, T> { /// Creates a new window builder with the given name - pub fn new(name: &ImStr) -> Window { + pub fn new(name: T) -> Self { Window { name, opened: None, @@ -526,7 +525,7 @@ impl<'a> Window<'a> { } let should_render = unsafe { sys::igBegin( - self.name.as_ptr(), + ui.scratch_txt(self.name), self.opened .map(|x| x as *mut bool) .unwrap_or(ptr::null_mut()), @@ -545,7 +544,7 @@ impl<'a> Window<'a> { /// /// Note: the closure is not called if no window content is visible (e.g. window is collapsed /// or fully clipped). - pub fn build T>(self, ui: &Ui<'_>, f: F) -> Option { + pub fn build R>(self, ui: &Ui<'_>, f: F) -> Option { self.begin(ui).map(|_window| f()) } } From 694cd96d497694b3c616804f4fc6c99663acf8b6 Mon Sep 17 00:00:00 2001 From: Jack Mac Date: Sat, 11 Sep 2021 19:03:20 -0400 Subject: [PATCH 09/21] getting close! input_text resize is broken, and i'll need to check the imgui docs example with std::string to get a reference to how to do it properly. --- imgui-examples/examples/keyboard.rs | 2 +- imgui-examples/examples/test_window_impl.rs | 24 ++- imgui-examples/examples/text_callbacks.rs | 17 +- imgui-gfx-renderer/src/lib.rs | 8 +- imgui-glium-renderer/src/lib.rs | 6 +- imgui-winit-support/src/lib.rs | 6 +- imgui/src/columns.rs | 5 +- imgui/src/context.rs | 55 ++++--- imgui/src/drag_drop.rs | 43 +++-- imgui/src/input_widget.rs | 171 ++++++++++---------- imgui/src/lib.rs | 122 +++++++------- imgui/src/plothistogram.rs | 57 ++++--- imgui/src/string.rs | 71 +++++++- imgui/src/widget/drag.rs | 151 +++++++++++------ 14 files changed, 436 insertions(+), 302 deletions(-) diff --git a/imgui-examples/examples/keyboard.rs b/imgui-examples/examples/keyboard.rs index bf09785..1114c0c 100644 --- a/imgui-examples/examples/keyboard.rs +++ b/imgui-examples/examples/keyboard.rs @@ -11,7 +11,7 @@ fn main() { let mut uncaptured_counter = 0u32; let mut home_counter = 0u32; let mut f1_release_count = 0u32; - let mut text_buffer = ImString::new(""); + let mut text_buffer = String::new(); system.main_loop(move |_, ui| { Window::new(im_str!("Means of accessing key state")) diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 0398b84..29e1acf 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -25,13 +25,13 @@ struct State { no_menu: bool, no_close: bool, wrap_width: f32, - buf: ImString, + buf: String, item: usize, item2: usize, item3: i32, - text: ImString, - text_with_hint: ImString, - text_multiline: ImString, + text: String, + text_with_hint: String, + text_multiline: String, i0: i32, f0: f32, vec2f: [f32; 2], @@ -57,12 +57,12 @@ struct State { impl Default for State { fn default() -> Self { - let mut buf = ImString::with_capacity(32); + let mut buf = String::with_capacity(32); buf.push_str("日本語"); - let mut text = ImString::with_capacity(128); + let mut text = String::with_capacity(128); text.push_str("Hello, world!"); - let text_with_hint = ImString::with_capacity(128); - let mut text_multiline = ImString::with_capacity(128); + let text_with_hint = String::with_capacity(128); + let mut text_multiline = String::with_capacity(128); text_multiline.push_str("Hello, world!\nMultiline"); State { show_app_main_menu_bar: false, @@ -873,9 +873,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { } menu.end(); } - MenuItem::new(im_str!("Save")) - .shortcut("Ctrl+S") - .build(ui); + MenuItem::new(im_str!("Save")).shortcut("Ctrl+S").build(ui); MenuItem::new(im_str!("Save As..")).build(ui); ui.separator(); if let Some(menu) = ui.begin_menu(im_str!("Options")) { @@ -908,9 +906,7 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { .begin_menu_with_enabled(im_str!("Disabled"), false) .is_none()); MenuItem::new(im_str!("Checked")).selected(true).build(ui); - MenuItem::new(im_str!("Quit")) - .shortcut("Alt+F4") - .build(ui); + MenuItem::new(im_str!("Quit")).shortcut("Alt+F4").build(ui); } fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &mut bool) { diff --git a/imgui-examples/examples/text_callbacks.rs b/imgui-examples/examples/text_callbacks.rs index 43c4ec3..0f92955 100644 --- a/imgui-examples/examples/text_callbacks.rs +++ b/imgui-examples/examples/text_callbacks.rs @@ -4,11 +4,7 @@ mod support; fn main() { let system = support::init(file!()); - let mut buffers = vec![ - ImString::default(), - ImString::default(), - ImString::default(), - ]; + let mut buffers = vec![String::default(), String::default(), String::default()]; system.main_loop(move |_, ui| { Window::new(im_str!("Input text callbacks")) @@ -70,13 +66,13 @@ fn main() { ui.separator(); ui.text("You can also define a callback on structs with data."); - ui.text("Here we implement the callback handler on a wrapper around &mut ImString"); + ui.text("Here we implement the callback handler on a wrapper around &mut String"); ui.text("to duplicate edits to buf0 on buf1"); - struct Wrapper<'a>(&'a mut ImString); + struct Wrapper<'a>(&'a mut String); impl<'a> InputTextCallbackHandler for Wrapper<'a> { fn on_always(&mut self, data: TextCallbackData<'_>) { - *self.0 = im_str!("{}", data.str()); + *self.0 = data.str().to_owned(); } } @@ -137,10 +133,7 @@ fn main() { } ui.input_text(im_str!("Wild buf2 editor"), buf2) - .callback( - InputTextCallback::HISTORY, - Wrapper2(buf0.to_str(), buf1.to_str()), - ) + .callback(InputTextCallback::HISTORY, Wrapper2(buf0, buf1)) .build(); ui.text( diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 7e9345e..e479ae1 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -10,9 +10,7 @@ use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; use imgui::internal::RawWrapper; -use imgui::{ - BackendFlags, DrawCmd, DrawCmdParams, DrawData, DrawIdx, ImString, TextureId, Textures, -}; +use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, DrawIdx, TextureId, Textures}; use std::error::Error; use std::fmt; use std::usize; @@ -178,10 +176,10 @@ where instances: None, buffer: index_buffer.clone().into_index_buffer(factory), }; - ctx.set_renderer_name(Some(ImString::from(format!( + ctx.set_renderer_name(Some(format!( "imgui-gfx-renderer {}", env!("CARGO_PKG_VERSION") - )))); + ))); ctx.io_mut() .backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index 72d4aa6..723fae9 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -13,7 +13,7 @@ use glium::{ Surface, Texture2d, VertexBuffer, }; use imgui::internal::RawWrapper; -use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, ImString, TextureId, Textures}; +use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawData, TextureId, Textures}; use std::borrow::Cow; use std::error::Error; use std::fmt; @@ -143,10 +143,10 @@ impl Renderer { ) -> Result { let program = compile_default_program(facade)?; let font_texture = upload_font_texture(ctx.fonts(), facade.get_context())?; - ctx.set_renderer_name(Some(ImString::from(format!( + ctx.set_renderer_name(Some(format!( "imgui-glium-renderer {}", env!("CARGO_PKG_VERSION") - )))); + ))); ctx.io_mut() .backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index cc6d44f..126c2c0 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -174,7 +174,7 @@ use winit_20 as winit; ))] use winit_19 as winit; -use imgui::{self, BackendFlags, ConfigFlags, Context, ImString, Io, Key, Ui}; +use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; use std::cell::Cell; use std::cmp::Ordering; use winit::dpi::{LogicalPosition, LogicalSize}; @@ -459,10 +459,10 @@ impl WinitPlatform { io[Key::X] = VirtualKeyCode::X as _; io[Key::Y] = VirtualKeyCode::Y as _; io[Key::Z] = VirtualKeyCode::Z as _; - imgui.set_platform_name(Some(ImString::from(format!( + imgui.set_platform_name(Some(format!( "imgui-winit-support {}", env!("CARGO_PKG_VERSION") - )))); + ))); WinitPlatform { hidpi_mode: ActiveHiDpiMode::Default, hidpi_factor: 1.0, diff --git a/imgui/src/columns.rs b/imgui/src/columns.rs index 2a631d5..b282e91 100644 --- a/imgui/src/columns.rs +++ b/imgui/src/columns.rs @@ -1,12 +1,11 @@ -use crate::string::ImStr; use crate::sys; use crate::Ui; /// # Columns impl<'ui> Ui<'ui> { #[doc(alias = "Columns")] - pub fn columns(&self, count: i32, id: &ImStr, border: bool) { - unsafe { sys::igColumns(count, id.as_ptr(), border) } + pub fn columns(&self, count: i32, id: impl AsRef, border: bool) { + unsafe { sys::igColumns(count, self.scratch_txt(id), border) } } /// Switches to the next column. /// diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 4f190d4..11b8cca 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -1,6 +1,6 @@ use parking_lot::ReentrantMutex; use std::cell::RefCell; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::ops::Drop; use std::path::PathBuf; use std::ptr; @@ -9,7 +9,6 @@ use std::rc::Rc; use crate::clipboard::{ClipboardBackend, ClipboardContext}; use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas}; use crate::io::Io; -use crate::string::{ImStr, ImString}; use crate::style::Style; use crate::sys; use crate::Ui; @@ -52,10 +51,10 @@ use crate::Ui; pub struct Context { raw: *mut sys::ImGuiContext, shared_font_atlas: Option>>, - ini_filename: Option, - log_filename: Option, - platform_name: Option, - renderer_name: Option, + ini_filename: Option, + log_filename: Option, + platform_name: Option, + renderer_name: Option, clipboard_ctx: Option, } @@ -109,18 +108,17 @@ impl Context { if io.ini_filename.is_null() { None } else { - let s = unsafe { ImStr::from_ptr_unchecked(io.ini_filename) }; - Some(PathBuf::from(s.to_str().to_owned())) + let s = unsafe { CStr::from_ptr(io.ini_filename) }; + Some(PathBuf::from(s.to_str().ok()?)) } } /// Sets the path to the ini file (default is "imgui.ini") /// /// Pass None to disable automatic .Ini saving. pub fn set_ini_filename>>(&mut self, ini_filename: T) { - let ini_filename = ini_filename - .into() - .and_then(|path| path.to_str().map(str::to_owned)) - .map(ImString::from); + let ini_filename: Option = ini_filename.into(); + let ini_filename = ini_filename.and_then(|v| CString::new(v.to_str()?).ok()); + self.io_mut().ini_filename = ini_filename .as_ref() .map(|x| x.as_ptr()) @@ -128,21 +126,22 @@ impl Context { self.ini_filename = ini_filename; } /// Returns the path to the log file, or None if not set + // TODO: why do we return an `Option` instead of an `Option<&Path>`? pub fn log_filename(&self) -> Option { let io = self.io(); if io.log_filename.is_null() { None } else { - let s = unsafe { ImStr::from_ptr_unchecked(io.log_filename) }; - Some(PathBuf::from(s.to_str().to_owned())) + let cstr = unsafe { CStr::from_ptr(io.log_filename) }; + Some(PathBuf::from(cstr.to_str().ok()?)) } } /// Sets the log filename (default is "imgui_log.txt"). pub fn set_log_filename>>(&mut self, log_filename: T) { let log_filename = log_filename .into() - .and_then(|path| path.to_str().map(str::to_owned)) - .map(ImString::from); + .and_then(|v| CString::new(v.to_str()?).ok()); + self.io_mut().log_filename = log_filename .as_ref() .map(|x| x.as_ptr()) @@ -150,17 +149,19 @@ impl Context { self.log_filename = log_filename; } /// Returns the backend platform name, or None if not set - pub fn platform_name(&self) -> Option<&ImStr> { + pub fn platform_name(&self) -> Option<&str> { let io = self.io(); if io.backend_platform_name.is_null() { None } else { - unsafe { Some(ImStr::from_ptr_unchecked(io.backend_platform_name)) } + let cstr = unsafe { CStr::from_ptr(io.backend_platform_name) }; + cstr.to_str().ok() } } /// Sets the backend platform name - pub fn set_platform_name>>(&mut self, platform_name: T) { - let platform_name = platform_name.into(); + pub fn set_platform_name>>(&mut self, platform_name: T) { + let platform_name: Option = + platform_name.into().and_then(|v| CString::new(v).ok()); self.io_mut().backend_platform_name = platform_name .as_ref() .map(|x| x.as_ptr()) @@ -168,21 +169,25 @@ impl Context { self.platform_name = platform_name; } /// Returns the backend renderer name, or None if not set - pub fn renderer_name(&self) -> Option<&ImStr> { + pub fn renderer_name(&self) -> Option<&str> { let io = self.io(); if io.backend_renderer_name.is_null() { None } else { - unsafe { Some(ImStr::from_ptr_unchecked(io.backend_renderer_name)) } + let cstr = unsafe { CStr::from_ptr(io.backend_renderer_name) }; + cstr.to_str().ok() } } /// Sets the backend renderer name - pub fn set_renderer_name>>(&mut self, renderer_name: T) { - let renderer_name = renderer_name.into(); + pub fn set_renderer_name>>(&mut self, renderer_name: T) { + let renderer_name: Option = + renderer_name.into().and_then(|v| CString::new(v).ok()); + self.io_mut().backend_renderer_name = renderer_name .as_ref() .map(|x| x.as_ptr()) .unwrap_or(ptr::null()); + self.renderer_name = renderer_name; } /// Loads settings from a string slice containing settings in .Ini file format @@ -527,7 +532,7 @@ impl Context { Ui { ctx: self, font_atlas, - buffer: Vec::new().into(), + buffer: crate::UiBuffer::new(1024).into(), } } } diff --git a/imgui/src/drag_drop.rs b/imgui/src/drag_drop.rs index a4ca0d9..ed4d6b4 100644 --- a/imgui/src/drag_drop.rs +++ b/imgui/src/drag_drop.rs @@ -28,7 +28,7 @@ //! For examples of each payload type, see [DragDropSource]. use std::{any, ffi, marker::PhantomData}; -use crate::{sys, Condition, ImStr, Ui}; +use crate::{sys, Condition, Ui}; use bitflags::bitflags; bitflags!( @@ -92,18 +92,17 @@ bitflags!( /// will manage, and then give to a [DragDropTarget], which will received the payload. The /// simplest and safest Payload is the empty payload, created with [begin](Self::begin). #[derive(Debug)] -pub struct DragDropSource<'a> { - name: &'a ImStr, +pub struct DragDropSource { + name: T, flags: DragDropFlags, cond: Condition, } -impl<'a> DragDropSource<'a> { +impl> DragDropSource { /// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name. /// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up /// Source/Target for DragDrop. - #[inline] - pub const fn new(name: &'a ImStr) -> Self { + pub fn new(name: T) -> Self { Self { name, flags: DragDropFlags::empty(), @@ -116,14 +115,14 @@ impl<'a> DragDropSource<'a> { /// `SOURCE_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will /// be accepted without panic. #[inline] - pub const fn flags(mut self, flags: DragDropFlags) -> Self { + pub fn flags(mut self, flags: DragDropFlags) -> Self { self.flags = flags; self } /// Sets the condition on the [DragDropSource]. Defaults to [Always](Condition::Always). #[inline] - pub const fn condition(mut self, cond: Condition) -> Self { + pub fn condition(mut self, cond: Condition) -> Self { self.cond = cond; self } @@ -223,17 +222,17 @@ impl<'a> DragDropSource<'a> { /// } /// ``` #[inline] - pub fn begin_payload<'ui, T: Copy + 'static>( + pub fn begin_payload<'ui, P: Copy + 'static>( self, ui: &Ui<'ui>, - payload: T, + payload: P, ) -> Option> { unsafe { let payload = TypedPayload::new(payload); self.begin_payload_unchecked( ui, &payload as *const _ as *const ffi::c_void, - std::mem::size_of::>(), + std::mem::size_of::>(), ) } } @@ -267,14 +266,14 @@ impl<'a> DragDropSource<'a> { #[inline] pub unsafe fn begin_payload_unchecked<'ui>( &self, - _ui: &Ui<'ui>, + ui: &Ui<'ui>, ptr: *const ffi::c_void, size: usize, ) -> Option> { let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32); if should_begin { - sys::igSetDragDropPayload(self.name.as_ptr(), ptr, size, self.cond as i32); + sys::igSetDragDropPayload(ui.scratch_txt(&self.name), ptr, size, self.cond as i32); Some(DragDropSourceToolTip::push()) } else { @@ -340,17 +339,17 @@ impl Drop for DragDropSourceToolTip<'_> { /// on this struct. Each of these methods will spit out a _Payload struct with an increasing /// amount of information on the Payload. The absolute safest solution is [accept_payload_empty](Self::accept_payload_empty). #[derive(Debug)] -pub struct DragDropTarget<'ui>(PhantomData>); +pub struct DragDropTarget<'ui>(&'ui Ui<'ui>); impl<'ui> DragDropTarget<'ui> { /// Creates a new DragDropTarget, holding the [Ui]'s lifetime for the duration /// of its existence. This is required since this struct runs some code on its Drop /// to end the DragDropTarget code. #[doc(alias = "BeginDragDropTarget")] - pub fn new(_ui: &Ui<'_>) -> Option { + pub fn new(ui: &'ui Ui<'ui>) -> Option { let should_begin = unsafe { sys::igBeginDragDropTarget() }; if should_begin { - Some(Self(PhantomData)) + Some(Self(ui)) } else { None } @@ -364,12 +363,12 @@ impl<'ui> DragDropTarget<'ui> { /// to use this function. Use `accept_payload_unchecked` instead pub fn accept_payload_empty( &self, - name: &ImStr, + name: impl AsRef, flags: DragDropFlags, ) -> Option { - self.accept_payload::<()>(name, flags)? + self.accept_payload(name, flags)? .ok() - .map(|payload_pod| DragDropPayloadEmpty { + .map(|payload_pod: DragDropPayloadPod<()>| DragDropPayloadEmpty { preview: payload_pod.preview, delivery: payload_pod.delivery, }) @@ -382,7 +381,7 @@ impl<'ui> DragDropTarget<'ui> { /// to use this function. Use `accept_payload_unchecked` instead pub fn accept_payload( &self, - name: &ImStr, + name: impl AsRef, flags: DragDropFlags, ) -> Option, PayloadIsWrongType>> { let output = unsafe { self.accept_payload_unchecked(name, flags) }; @@ -435,10 +434,10 @@ impl<'ui> DragDropTarget<'ui> { /// of the various edge cases. pub unsafe fn accept_payload_unchecked( &self, - name: &ImStr, + name: impl AsRef, flags: DragDropFlags, ) -> Option { - let inner = sys::igAcceptDragDropPayload(name.as_ptr(), flags.bits() as i32); + let inner = sys::igAcceptDragDropPayload(self.0.scratch_txt(name), flags.bits() as i32); if inner.is_null() { None } else { diff --git a/imgui/src/input_widget.rs b/imgui/src/input_widget.rs index 5dd6290..7ceed36 100644 --- a/imgui/src/input_widget.rs +++ b/imgui/src/input_widget.rs @@ -1,10 +1,9 @@ use bitflags::bitflags; -use std::marker::PhantomData; use std::ops::Range; use std::os::raw::{c_char, c_int, c_void}; use crate::sys; -use crate::{ImStr, ImString, Ui}; +use crate::Ui; bitflags!( /// Flags for text inputs @@ -160,17 +159,17 @@ macro_rules! impl_step_params { } #[must_use] -pub struct InputText<'ui, 'p, T = PassthroughCallback> { - label: &'p ImStr, - hint: Option<&'p ImStr>, - buf: &'p mut ImString, +pub struct InputText<'ui, 'p, L, H = &'static str, T = PassthroughCallback> { + label: L, + hint: Option, + buf: &'p mut String, callback_handler: T, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputText<'ui, 'p, PassthroughCallback> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString) -> Self { +impl<'ui, 'p, L: AsRef> InputText<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String) -> Self { InputText { label, hint: None, @@ -178,17 +177,28 @@ impl<'ui, 'p> InputText<'ui, 'p, PassthroughCallback> { callback_handler: PassthroughCallback, buf, flags: InputTextFlags::CALLBACK_RESIZE, - _phantom: PhantomData, + ui, } } } -impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { +impl<'ui, 'p, T, L, H> InputText<'ui, 'p, L, H, T> +where + L: AsRef, + H: AsRef, + T: InputTextCallbackHandler, +{ /// Sets the hint displayed in the input text background. #[inline] - pub fn hint(mut self, hint: &'p ImStr) -> Self { - self.hint = Some(hint); - self + pub fn hint>(self, hint: H2) -> InputText<'ui, 'p, L, H2, T> { + InputText { + label: self.label, + hint: Some(hint), + buf: self.buf, + callback_handler: self.callback_handler, + flags: self.flags, + ui: self.ui, + } } impl_text_flags!(InputText); @@ -209,7 +219,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { mut self, callbacks: InputTextCallback, callback: T2, - ) -> InputText<'ui, 'p, T2> { + ) -> InputText<'ui, 'p, L, H, T2> { if callbacks.contains(InputTextCallback::COMPLETION) { self.flags.insert(InputTextFlags::CALLBACK_COMPLETION); } @@ -231,12 +241,12 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { hint: self.hint, buf: self.buf, flags: self.flags, - _phantom: self._phantom, + ui: self.ui, } } pub fn build(self) -> bool { - let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); + let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { container: self.buf, @@ -245,57 +255,57 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputText<'ui, 'p, T> { let data = &mut data as *mut _ as *mut c_void; unsafe { - let result = if let Some(hint) = self.hint { + if let Some(hint) = self.hint { + let (label, hint) = self.ui.scratch_txt_two(self.label, hint); sys::igInputTextWithHint( - self.label.as_ptr(), - hint.as_ptr(), - ptr, + label, + hint, + ptr as *mut sys::cty::c_char, capacity, self.flags.bits() as i32, Some(callback::), data, ) } else { + let label = self.ui.scratch_txt(self.label); + sys::igInputText( - self.label.as_ptr(), - ptr, + label, + ptr as *mut sys::cty::c_char, capacity, self.flags.bits() as i32, Some(callback::), data, ) - }; - self.buf.refresh_len(); - result + } } } } #[must_use] -pub struct InputTextMultiline<'ui, 'p, T = PassthroughCallback> { - label: &'p ImStr, - buf: &'p mut ImString, +pub struct InputTextMultiline<'ui, 'p, L, T = PassthroughCallback> { + label: L, + buf: &'p mut String, flags: InputTextFlags, size: [f32; 2], callback_handler: T, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputTextMultiline<'ui, 'p, PassthroughCallback> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, buf: &'p mut ImString, size: [f32; 2]) -> Self { +impl<'ui, 'p, L: AsRef> InputTextMultiline<'ui, 'p, L, PassthroughCallback> { + pub fn new(ui: &'ui Ui<'ui>, label: L, buf: &'p mut String, size: [f32; 2]) -> Self { InputTextMultiline { label, buf, flags: InputTextFlags::CALLBACK_RESIZE, size, - // this is safe because callback_handler: PassthroughCallback, - _phantom: PhantomData, + ui, } } } -impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { +impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef> InputTextMultiline<'ui, 'p, L, T> { impl_text_flags!(InputText); /// By default (as of 0.8.0), imgui-rs will automatically handle string resizes @@ -314,7 +324,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { mut self, callbacks: InputTextMultilineCallback, callback_handler: T2, - ) -> InputTextMultiline<'ui, 'p, T2> { + ) -> InputTextMultiline<'ui, 'p, L, T2> { if callbacks.contains(InputTextMultilineCallback::COMPLETION) { self.flags.insert(InputTextFlags::CALLBACK_COMPLETION); } @@ -334,12 +344,12 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { flags: self.flags, size: self.size, callback_handler, - _phantom: self._phantom, + ui: self.ui, } } pub fn build(self) -> bool { - let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity_with_nul()); + let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { container: self.buf, @@ -348,47 +358,45 @@ impl<'ui, 'p, T: InputTextCallbackHandler> InputTextMultiline<'ui, 'p, T> { let data = &mut data as *mut _ as *mut c_void; unsafe { - let result = sys::igInputTextMultiline( - self.label.as_ptr(), - ptr, + sys::igInputTextMultiline( + self.ui.scratch_txt(self.label), + ptr as *mut sys::cty::c_char, capacity, self.size.into(), self.flags.bits() as i32, Some(callback::), data, - ); - self.buf.refresh_len(); - result + ) } } } #[must_use] -pub struct InputInt<'ui, 'p> { - label: &'p ImStr, +pub struct InputInt<'ui, 'p, L> { + label: L, value: &'p mut i32, step: i32, step_fast: i32, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputInt<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut i32) -> Self { +impl<'ui, 'p, L: AsRef> InputInt<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut i32) -> Self { InputInt { label, value, step: 1, step_fast: 100, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::igInputInt( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value as *mut i32, self.step, self.step_fast, @@ -402,31 +410,31 @@ impl<'ui, 'p> InputInt<'ui, 'p> { } #[must_use] -pub struct InputFloat<'ui, 'p> { - label: &'p ImStr, +pub struct InputFloat<'ui, 'p, L> { + label: L, value: &'p mut f32, step: f32, step_fast: f32, flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> InputFloat<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut f32) -> Self { +impl<'ui, 'p, L: AsRef> InputFloat<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut f32) -> Self { InputFloat { label, value, step: 0.0, step_fast: 0.0, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::igInputFloat( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value as *mut f32, self.step, self.step_fast, @@ -443,27 +451,27 @@ impl<'ui, 'p> InputFloat<'ui, 'p> { macro_rules! impl_input_floatn { ($InputFloatN:ident, $N:expr, $igInputFloatN:ident) => { #[must_use] - pub struct $InputFloatN<'ui, 'p> { - label: &'p ImStr, + pub struct $InputFloatN<'ui, 'p, L> { + label: L, value: &'p mut [f32; $N], flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } - impl<'ui, 'p> $InputFloatN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [f32; $N]) -> Self { + impl<'ui, 'p, L: AsRef> $InputFloatN<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [f32; $N]) -> Self { $InputFloatN { label, value, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::$igInputFloatN( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value.as_mut_ptr(), b"%.3f\0".as_ptr() as *const _, self.flags.bits() as i32, @@ -483,27 +491,27 @@ impl_input_floatn!(InputFloat4, 4, igInputFloat4); macro_rules! impl_input_intn { ($InputIntN:ident, $N:expr, $igInputIntN:ident) => { #[must_use] - pub struct $InputIntN<'ui, 'p> { - label: &'p ImStr, + pub struct $InputIntN<'ui, 'p, L> { + label: L, value: &'p mut [i32; $N], flags: InputTextFlags, - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } - impl<'ui, 'p> $InputIntN<'ui, 'p> { - pub fn new(_: &Ui<'ui>, label: &'p ImStr, value: &'p mut [i32; $N]) -> Self { + impl<'ui, 'p, L: AsRef> $InputIntN<'ui, 'p, L> { + pub fn new(ui: &'ui Ui<'ui>, label: L, value: &'p mut [i32; $N]) -> Self { $InputIntN { label, value, flags: InputTextFlags::empty(), - _phantom: PhantomData, + ui, } } pub fn build(self) -> bool { unsafe { sys::$igInputIntN( - self.label.as_ptr(), + self.ui.scratch_txt(self.label), self.value.as_mut_ptr(), self.flags.bits() as i32, ) @@ -809,7 +817,7 @@ impl<'a> TextCallbackData<'a> { #[repr(C)] struct UserData<'a, T> { - container: &'a mut ImString, + container: &'a mut String, cback_handler: T, } @@ -851,14 +859,15 @@ extern "C" fn callback( unsafe { let requested_size = (*data).BufSize as usize; let buffer = &mut callback_data.user_data.container; - if requested_size > buffer.capacity_with_nul() { - // Refresh the buffer's length to take into account changes made by dear imgui. - buffer.refresh_len(); - buffer.reserve(requested_size - buffer.0.len()); - debug_assert!(buffer.capacity_with_nul() >= requested_size); - (*data).Buf = buffer.as_mut_ptr(); - (*data).BufDirty = true; - } + todo!() + // if requested_size > buffer.capacity_with_nul() { + // // Refresh the buffer's length to take into account changes made by dear imgui. + // buffer.refresh_len(); + // buffer.reserve(requested_size - buffer.0.len()); + // debug_assert!(buffer.capacity_with_nul() >= requested_size); + // (*data).Buf = buffer.as_mut_ptr(); + // (*data).BufDirty = true; + // } } } InputTextFlags::CALLBACK_CHAR_FILTER => { diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 563883a..581f701 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -117,11 +117,12 @@ impl Context { } /// A temporary reference for building the user interface for one frame +#[derive(Debug)] pub struct Ui<'ui> { ctx: &'ui Context, font_atlas: Option>, - // imgui isn't mutli-threaded -- so no one will ever access - buffer: cell::UnsafeCell>, + // imgui isn't mutli-threaded -- so no one will ever access twice. + buffer: cell::UnsafeCell, } impl<'ui> Ui<'ui> { @@ -129,19 +130,15 @@ impl<'ui> Ui<'ui> { 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 _ + handle.scratch_txt(txt) } } /// Internal method to push an option text to our scratch buffer. fn scratch_txt_opt(&self, txt: Option>) -> *const sys::cty::c_char { - match txt { - Some(v) => self.scratch_txt(v), - None => std::ptr::null(), + unsafe { + let handle = &mut *self.buffer.get(); + handle.scratch_txt_opt(txt) } } @@ -152,16 +149,7 @@ impl<'ui> Ui<'ui> { ) -> (*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 _, - ) + handle.scratch_txt_two(txt_0, txt_1) } } @@ -172,21 +160,7 @@ impl<'ui> Ui<'ui> { ) -> (*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'); - - if let Some(txt_1) = txt_1 { - 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 _, - ) - } else { - (handle.as_ptr() as *const _, std::ptr::null()) - } + handle.scratch_txt_with_opt(txt_0, txt_1) } } @@ -317,59 +291,83 @@ impl From<*mut T> for Id<'static> { // Widgets: Input impl<'ui> Ui<'ui> { #[doc(alias = "InputText", alias = "InputTextWithHint")] - pub fn input_text<'p>(&self, label: &'p ImStr, buf: &'p mut ImString) -> InputText<'ui, 'p> { + pub fn input_text<'p, L: AsRef>( + &'ui self, + label: L, + buf: &'p mut String, + ) -> InputText<'ui, 'p, L> { InputText::new(self, label, buf) } #[doc(alias = "InputText", alias = "InputTextMultiline")] - pub fn input_text_multiline<'p>( - &self, - label: &'p ImStr, - buf: &'p mut ImString, + pub fn input_text_multiline<'p, L: AsRef>( + &'ui self, + label: L, + buf: &'p mut String, size: [f32; 2], - ) -> InputTextMultiline<'ui, 'p> { + ) -> InputTextMultiline<'ui, 'p, L> { InputTextMultiline::new(self, label, buf, size) } #[doc(alias = "InputFloat2")] - pub fn input_float<'p>(&self, label: &'p ImStr, value: &'p mut f32) -> InputFloat<'ui, 'p> { + pub fn input_float<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut f32, + ) -> InputFloat<'ui, 'p, L> { InputFloat::new(self, label, value) } - pub fn input_float2<'p>( - &self, - label: &'p ImStr, + pub fn input_float2<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 2], - ) -> InputFloat2<'ui, 'p> { + ) -> InputFloat2<'ui, 'p, L> { InputFloat2::new(self, label, value) } #[doc(alias = "InputFloat3")] - pub fn input_float3<'p>( - &self, - label: &'p ImStr, + pub fn input_float3<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 3], - ) -> InputFloat3<'ui, 'p> { + ) -> InputFloat3<'ui, 'p, L> { InputFloat3::new(self, label, value) } #[doc(alias = "InputFloat4")] - pub fn input_float4<'p>( - &self, - label: &'p ImStr, + pub fn input_float4<'p, L: AsRef>( + &'ui self, + label: L, value: &'p mut [f32; 4], - ) -> InputFloat4<'ui, 'p> { + ) -> InputFloat4<'ui, 'p, L> { InputFloat4::new(self, label, value) } #[doc(alias = "InputInt")] - pub fn input_int<'p>(&self, label: &'p ImStr, value: &'p mut i32) -> InputInt<'ui, 'p> { + pub fn input_int<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut i32, + ) -> InputInt<'ui, 'p, L> { InputInt::new(self, label, value) } #[doc(alias = "InputInt2")] - pub fn input_int2<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 2]) -> InputInt2<'ui, 'p> { + pub fn input_int2<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 2], + ) -> InputInt2<'ui, 'p, L> { InputInt2::new(self, label, value) } #[doc(alias = "InputInt3")] - pub fn input_int3<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 3]) -> InputInt3<'ui, 'p> { + pub fn input_int3<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 3], + ) -> InputInt3<'ui, 'p, L> { InputInt3::new(self, label, value) } #[doc(alias = "InputInt4")] - pub fn input_int4<'p>(&self, label: &'p ImStr, value: &'p mut [i32; 4]) -> InputInt4<'ui, 'p> { + pub fn input_int4<'p, L: AsRef>( + &'ui self, + label: L, + value: &'p mut [i32; 4], + ) -> InputInt4<'ui, 'p, L> { InputInt4::new(self, label, value) } } @@ -470,11 +468,11 @@ impl<'ui> Ui<'ui> { impl<'ui> Ui<'ui> { #[doc(alias = "PlotHistogram")] - pub fn plot_histogram<'p>( - &self, - label: &'p ImStr, + pub fn plot_histogram<'p, Label: AsRef>( + &'ui self, + label: Label, values: &'p [f32], - ) -> PlotHistogram<'ui, 'p> { + ) -> PlotHistogram<'ui, 'p, Label> { PlotHistogram::new(self, label, values) } } diff --git a/imgui/src/plothistogram.rs b/imgui/src/plothistogram.rs index e58c472..a88aabf 100644 --- a/imgui/src/plothistogram.rs +++ b/imgui/src/plothistogram.rs @@ -1,23 +1,22 @@ -use std::marker::PhantomData; use std::os::raw::c_float; -use std::{f32, mem, ptr}; +use std::{f32, mem}; -use super::{ImStr, Ui}; +use super::Ui; #[must_use] -pub struct PlotHistogram<'ui, 'p> { - label: &'p ImStr, +pub struct PlotHistogram<'ui, 'p, Label, Overlay = &'static str> { + label: Label, values: &'p [f32], values_offset: usize, - overlay_text: Option<&'p ImStr>, + overlay_text: Option, scale_min: f32, scale_max: f32, graph_size: [f32; 2], - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> PlotHistogram<'ui, 'p> { - pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { +impl<'ui, 'p, Label: AsRef> PlotHistogram<'ui, 'p, Label> { + pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self { PlotHistogram { label, values, @@ -26,48 +25,58 @@ impl<'ui, 'p> PlotHistogram<'ui, 'p> { scale_min: f32::MAX, scale_max: f32::MAX, graph_size: [0.0, 0.0], - _phantom: PhantomData, + ui, } } +} - #[inline] - pub const fn values_offset(mut self, values_offset: usize) -> Self { +impl<'ui, 'p, Label: AsRef, Overlay: AsRef> PlotHistogram<'ui, 'p, Label, Overlay> { + pub fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } - #[inline] - pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { - self.overlay_text = Some(overlay_text); - self + pub fn overlay_text>( + self, + overlay_text: NewOverlay, + ) -> PlotHistogram<'ui, 'p, Label, NewOverlay> { + PlotHistogram { + label: self.label, + values: self.values, + values_offset: self.values_offset, + overlay_text: Some(overlay_text), + scale_min: self.scale_min, + scale_max: self.scale_max, + graph_size: self.graph_size, + ui: self.ui, + } } - #[inline] - pub const fn scale_min(mut self, scale_min: f32) -> Self { + pub fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } - #[inline] - pub const fn scale_max(mut self, scale_max: f32) -> Self { + pub fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } - #[inline] - pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } pub fn build(self) { unsafe { + let (label, overlay_text) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text); + sys::igPlotHistogramFloatPtr( - self.label.as_ptr(), + label, self.values.as_ptr() as *const c_float, self.values.len() as i32, self.values_offset as i32, - self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()), + overlay_text, self.scale_min, self.scale_max, self.graph_size.into(), diff --git a/imgui/src/string.rs b/imgui/src/string.rs index 9b7d91a..a345ce7 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -1,9 +1,77 @@ use std::borrow::{Borrow, Cow}; use std::ffi::CStr; -use std::fmt; use std::ops::{Deref, Index, RangeFull}; use std::os::raw::c_char; use std::str; +use std::{fmt, ptr}; + +/// this is the unsafe cell upon which we build our abstraction. +pub(crate) struct UiBuffer { + buffer: Vec, + max_len: usize, +} + +impl UiBuffer { + /// Creates a new max buffer with the given length. + pub fn new(max_len: usize) -> Self { + Self { + buffer: Vec::with_capacity(max_len), + max_len, + } + } + + /// Internal method to push a single text to our scratch buffer. + pub fn scratch_txt(&mut self, txt: impl AsRef) -> *const sys::cty::c_char { + self.refresh_buffer(); + self.push(txt) + } + + /// Internal method to push an option text to our scratch buffer. + pub fn scratch_txt_opt(&mut self, txt: Option>) -> *const sys::cty::c_char { + match txt { + Some(v) => self.scratch_txt(v), + None => ptr::null(), + } + } + + pub fn scratch_txt_two( + &mut self, + txt_0: impl AsRef, + txt_1: impl AsRef, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + self.refresh_buffer(); + (self.push(txt_0), self.push(txt_1)) + } + + pub fn scratch_txt_with_opt( + &mut self, + txt_0: impl AsRef, + txt_1: Option>, + ) -> (*const sys::cty::c_char, *const sys::cty::c_char) { + match txt_1 { + Some(value) => self.scratch_txt_two(txt_0, value), + None => (self.scratch_txt(txt_0), ptr::null()), + } + } + + /// Attempts to clear the buffer if it's over the maximum length allowed. + pub fn refresh_buffer(&mut self) { + if self.buffer.len() > self.max_len { + self.buffer.clear(); + } + } + + /// Pushes a new scratch sheet text, which means it's not handling any clearing at all. + pub fn push(&mut self, txt: impl AsRef) -> *const sys::cty::c_char { + unsafe { + let len = self.buffer.len(); + self.buffer.extend(txt.as_ref().as_bytes()); + self.buffer.push(b'\0'); + + self.buffer.as_ptr().add(len) as *const _ + } + } +} #[macro_export] macro_rules! im_str { @@ -277,6 +345,7 @@ impl fmt::Write for ImString { /// A UTF-8 encoded, implicitly nul-terminated string slice. #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] +#[deprecated] pub struct ImStr([u8]); impl<'a> Default for &'a ImStr { diff --git a/imgui/src/widget/drag.rs b/imgui/src/widget/drag.rs index 3737220..6f9c531 100644 --- a/imgui/src/widget/drag.rs +++ b/imgui/src/widget/drag.rs @@ -2,7 +2,6 @@ use std::os::raw::c_void; use std::ptr; use crate::internal::DataTypeKind; -use crate::string::ImStr; use crate::sys; use crate::widget::slider::SliderFlags; use crate::Ui; @@ -10,16 +9,16 @@ use crate::Ui; /// Builder for a drag slider widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct Drag<'a, T, L> { +pub struct Drag { label: L, speed: f32, min: Option, max: Option, - display_format: Option<&'a str>, + display_format: Option, flags: SliderFlags, } -impl<'a, L: AsRef, T: DataTypeKind> Drag<'a, T, L> { +impl, T: DataTypeKind> Drag { /// Constructs a new drag slider builder. #[doc(alias = "DragScalar", alias = "DragScalarN")] pub fn new(label: L) -> Self { @@ -32,8 +31,10 @@ impl<'a, L: AsRef, T: DataTypeKind> Drag<'a, T, L> { flags: SliderFlags::empty(), } } +} + +impl, T: DataTypeKind, F: AsRef> Drag { /// Sets the range (inclusive) - #[inline] pub fn range(mut self, min: T, max: T) -> Self { self.min = Some(min); self.max = Some(max); @@ -42,19 +43,22 @@ impl<'a, L: AsRef, T: DataTypeKind> Drag<'a, T, L> { /// Sets the value increment for a movement of one pixel. /// /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1 - #[inline] pub fn speed(mut self, speed: f32) -> Self { self.speed = speed; self } /// Sets the display format using *a C-style printf string* - #[inline] - pub fn display_format(mut self, display_format: &'a str) -> Self { - self.display_format = Some(display_format); - self + pub fn display_format>(self, display_format: F2) -> Drag { + Drag { + label: self.label, + speed: self.speed, + min: self.min, + max: self.max, + display_format: Some(display_format), + flags: self.flags, + } } /// Replaces all current settings with the given flags - #[inline] pub fn flags(mut self, flags: SliderFlags) -> Self { self.flags = flags; self @@ -115,20 +119,20 @@ impl<'a, L: AsRef, T: DataTypeKind> Drag<'a, T, L> { /// Builder for a drag slider widget. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct DragRange<'a, T: DataTypeKind> { - label: &'a ImStr, +pub struct DragRange { + label: L, speed: f32, min: Option, max: Option, - display_format: Option<&'a ImStr>, - max_display_format: Option<&'a ImStr>, + display_format: Option, + max_display_format: Option, flags: SliderFlags, } -impl<'a, T: DataTypeKind> DragRange<'a, T> { +impl> DragRange { /// Constructs a new drag slider builder. #[doc(alias = "DragIntRange2", alias = "DragFloatRange2")] - pub fn new(label: &ImStr) -> DragRange { + pub fn new(label: L) -> DragRange { DragRange { label, speed: 1.0, @@ -139,7 +143,15 @@ impl<'a, T: DataTypeKind> DragRange<'a, T> { flags: SliderFlags::empty(), } } - #[inline] +} + +impl DragRange +where + T: DataTypeKind, + L: AsRef, + F: AsRef, + M: AsRef, +{ pub fn range(mut self, min: T, max: T) -> Self { self.min = Some(min); self.max = Some(max); @@ -148,77 +160,124 @@ impl<'a, T: DataTypeKind> DragRange<'a, T> { /// Sets the value increment for a movement of one pixel. /// /// Example: speed=0.2 means mouse needs to move 5 pixels to increase the slider value by 1 - #[inline] pub fn speed(mut self, speed: f32) -> Self { self.speed = speed; self } /// Sets the display format using *a C-style printf string* - #[inline] - pub fn display_format(mut self, display_format: &'a ImStr) -> Self { - self.display_format = Some(display_format); - self + pub fn display_format>(self, display_format: F2) -> DragRange { + DragRange { + label: self.label, + speed: self.speed, + min: self.min, + max: self.max, + display_format: Some(display_format), + max_display_format: self.max_display_format, + flags: self.flags, + } } /// Sets the display format for the max value using *a C-style printf string* - #[inline] - pub fn max_display_format(mut self, max_display_format: &'a ImStr) -> Self { - self.max_display_format = Some(max_display_format); - self + pub fn max_display_format>( + self, + max_display_format: M2, + ) -> DragRange { + DragRange { + label: self.label, + speed: self.speed, + min: self.min, + max: self.max, + display_format: self.display_format, + max_display_format: Some(max_display_format), + flags: self.flags, + } } /// Replaces all current settings with the given flags - #[inline] pub fn flags(mut self, flags: SliderFlags) -> Self { self.flags = flags; self } } -impl<'a> DragRange<'a, f32> { +impl DragRange +where + L: AsRef, + F: AsRef, + M: AsRef, +{ /// Builds a drag range slider that is bound to the given min/max values. /// /// Returns true if the slider value was changed. #[doc(alias = "DragFloatRange2")] - pub fn build(self, _: &Ui, min: &mut f32, max: &mut f32) -> bool { + pub fn build(self, ui: &Ui, min: &mut f32, max: &mut f32) -> bool { + let label; + let mut display_format = std::ptr::null(); + let mut max_display_format = std::ptr::null(); + + // we do this ourselves the long way... unsafe { + let buffer = &mut *ui.buffer.get(); + buffer.refresh_buffer(); + + label = buffer.push(self.label); + if let Some(v) = self.display_format { + display_format = buffer.push(v); + } + if let Some(v) = self.max_display_format { + max_display_format = buffer.push(v); + } + sys::igDragFloatRange2( - self.label.as_ptr(), + label, min as *mut f32, max as *mut f32, self.speed, self.min.unwrap_or(0.0), self.max.unwrap_or(0.0), - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), - self.max_display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + display_format, + max_display_format, self.flags.bits() as i32, ) } } } -impl<'a> DragRange<'a, i32> { +impl DragRange +where + L: AsRef, + F: AsRef, + M: AsRef, +{ /// Builds a drag range slider that is bound to the given min/max values. /// /// Returns true if the slider value was changed. #[doc(alias = "DragIntRange2")] - pub fn build(self, _: &Ui, min: &mut i32, max: &mut i32) -> bool { + pub fn build(self, ui: &Ui, min: &mut i32, max: &mut i32) -> bool { unsafe { + let label; + let mut display_format = std::ptr::null(); + let mut max_display_format = std::ptr::null(); + + // we do this ourselves the long way... + let buffer = &mut *ui.buffer.get(); + buffer.refresh_buffer(); + + label = buffer.push(self.label); + if let Some(v) = self.display_format { + display_format = buffer.push(v); + } + if let Some(v) = self.max_display_format { + max_display_format = buffer.push(v); + } + sys::igDragIntRange2( - self.label.as_ptr(), + label, min as *mut i32, max as *mut i32, self.speed, self.min.unwrap_or(0), self.max.unwrap_or(0), - self.display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), - self.max_display_format - .map(ImStr::as_ptr) - .unwrap_or(ptr::null()), + display_format, + max_display_format, self.flags.bits() as i32, ) } From 3eaee3359d063fe5dbcb743ec01cf41b28632992 Mon Sep 17 00:00:00 2001 From: Jack Mac Date: Sun, 12 Sep 2021 02:01:07 -0400 Subject: [PATCH 10/21] imstr and imstring have been removed. fixed some instability and added a hacky shim to input_text functions. We're looking okay so far, but more testing will be needed --- imgui-examples/examples/support/clipboard.rs | 2 +- imgui-examples/examples/text_callbacks.rs | 24 ++++---- imgui-glow-renderer/src/lib.rs | 7 ++- imgui/src/clipboard.rs | 23 ++++++++ imgui/src/context.rs | 20 ++++--- imgui/src/input_widget.rs | 59 ++++++++++++++----- imgui/src/lib.rs | 60 ++++++++++++++++++-- imgui/src/plotlines.rs | 57 +++++++++++-------- imgui/src/popups.rs | 51 ++++++++--------- imgui/src/string.rs | 2 +- 10 files changed, 208 insertions(+), 97 deletions(-) diff --git a/imgui-examples/examples/support/clipboard.rs b/imgui-examples/examples/support/clipboard.rs index 297a696..613dbb5 100644 --- a/imgui-examples/examples/support/clipboard.rs +++ b/imgui-examples/examples/support/clipboard.rs @@ -1,7 +1,7 @@ use clipboard::{ClipboardContext, ClipboardProvider}; use imgui::ClipboardBackend; -pub struct ClipboardSupport(ClipboardContext); +pub struct ClipboardSupport(pub ClipboardContext); pub fn init() -> Option { ClipboardContext::new().ok().map(ClipboardSupport) diff --git a/imgui-examples/examples/text_callbacks.rs b/imgui-examples/examples/text_callbacks.rs index 0f92955..7c19523 100644 --- a/imgui-examples/examples/text_callbacks.rs +++ b/imgui-examples/examples/text_callbacks.rs @@ -7,7 +7,7 @@ fn main() { let mut buffers = vec![String::default(), String::default(), String::default()]; system.main_loop(move |_, ui| { - Window::new(im_str!("Input text callbacks")) + Window::new("Input text callbacks") .size([500.0, 300.0], Condition::FirstUseEver) .build(ui, || { ui.text("You can make a variety of buffer callbacks on an Input Text"); @@ -24,9 +24,10 @@ fn main() { ui.separator(); ui.text("No callbacks:"); - ui.input_text(im_str!("buf0"), &mut buffers[0]).build(); - ui.input_text(im_str!("buf0"), &mut buffers[1]).build(); - ui.input_text(im_str!("buf0"), &mut buffers[2]).build(); + + ui.input_text("buf0", &mut buffers[0]).build(); + ui.input_text("buf1", &mut buffers[1]).build(); + ui.input_text("buf2", &mut buffers[2]).build(); ui.separator(); @@ -50,18 +51,15 @@ fn main() { println!("History was fired by pressing {:?}", dir); } - fn on_always(&mut self, _: TextCallbackData<'_>) { + fn on_always(&mut self, txt: TextCallbackData<'_>) { // We don't actually print this out because it will flood your log a lot! // println!("The always callback fired! It always fires."); } } - ui.input_text( - im_str!("All Callbacks logging"), - buffers.get_mut(0).unwrap(), - ) - .callback(InputTextCallback::all(), AllCallback) - .build(); + ui.input_text("All Callbacks logging", buffers.get_mut(0).unwrap()) + .callback(InputTextCallback::all(), AllCallback) + .build(); ui.separator(); @@ -79,7 +77,7 @@ fn main() { let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap(); let buf1 = Wrapper(&mut brwchk_dance[0]); - ui.input_text(im_str!("Edits copied to buf1"), buf0) + ui.input_text("Edits copied to buf1", buf0) .callback(InputTextCallback::ALWAYS, buf1) .build(); @@ -132,7 +130,7 @@ fn main() { } } - ui.input_text(im_str!("Wild buf2 editor"), buf2) + ui.input_text("Wild buf2 editor", buf2) .callback(InputTextCallback::HISTORY, Wrapper2(buf0, buf1)) .build(); diff --git a/imgui-glow-renderer/src/lib.rs b/imgui-glow-renderer/src/lib.rs index 4379d16..f6d1814 100644 --- a/imgui-glow-renderer/src/lib.rs +++ b/imgui-glow-renderer/src/lib.rs @@ -513,9 +513,10 @@ impl Renderer { } fn configure_imgui_context(&self, imgui_context: &mut imgui::Context) { - imgui_context.set_renderer_name(Some( - format!("imgui-rs-glow-render {}", env!("CARGO_PKG_VERSION")).into(), - )); + imgui_context.set_renderer_name(Some(format!( + "imgui-rs-glow-render {}", + env!("CARGO_PKG_VERSION") + ))); #[cfg(feature = "vertex_offset_support")] if self.gl_version.vertex_offset_support() { diff --git a/imgui/src/clipboard.rs b/imgui/src/clipboard.rs index 2c9ac9c..7be4043 100644 --- a/imgui/src/clipboard.rs +++ b/imgui/src/clipboard.rs @@ -32,6 +32,24 @@ impl ClipboardContext { last_value: CString::default(), } } + + pub fn dummy() -> ClipboardContext { + Self { + backend: Box::new(DummyClipboardContext), + last_value: CString::default(), + } + } +} + +pub struct DummyClipboardContext; +impl ClipboardBackend for DummyClipboardContext { + fn get(&mut self) -> Option { + None + } + + fn set(&mut self, _: &str) { + // empty + } } impl fmt::Debug for ClipboardContext { @@ -46,9 +64,14 @@ impl fmt::Debug for ClipboardContext { pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char { let result = catch_unwind(|| { + println!("gettin!"); let ctx = &mut *(user_data as *mut ClipboardContext); + println!("gettin!"); + match ctx.backend.get() { Some(text) => { + println!("gettin!"); + ctx.last_value = CString::new(text).unwrap(); ctx.last_value.as_ptr() } diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 11b8cca..8139b3c 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -1,5 +1,5 @@ use parking_lot::ReentrantMutex; -use std::cell::RefCell; +use std::cell::{RefCell, UnsafeCell}; use std::ffi::{CStr, CString}; use std::ops::Drop; use std::path::PathBuf; @@ -55,7 +55,11 @@ pub struct Context { log_filename: Option, platform_name: Option, renderer_name: Option, - clipboard_ctx: Option, + // we need to box this because we hand imgui a pointer to it, + // and we don't want to deal with finding `clipboard_ctx`. + // we also put it in an unsafecell since we're going to give + // imgui a mutable pointer to it. + clipboard_ctx: Box>, } // This mutex needs to be used to guard all public functions that can affect the underlying @@ -203,13 +207,13 @@ impl Context { } /// Sets the clipboard backend used for clipboard operations pub fn set_clipboard_backend(&mut self, backend: T) { - use std::borrow::BorrowMut; - let mut clipboard_ctx = ClipboardContext::new(backend); + let clipboard_ctx: Box> = Box::new(ClipboardContext::new(backend).into()); let io = self.io_mut(); io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text); io.get_clipboard_text_fn = Some(crate::clipboard::get_clipboard_text); - io.clipboard_user_data = clipboard_ctx.borrow_mut() as *mut ClipboardContext as *mut _; - self.clipboard_ctx.replace(clipboard_ctx); + + io.clipboard_user_data = clipboard_ctx.get() as *mut _; + self.clipboard_ctx = clipboard_ctx; } fn create_internal(shared_font_atlas: Option>>) -> Self { let _guard = CTX_MUTEX.lock(); @@ -236,7 +240,7 @@ impl Context { log_filename: None, platform_name: None, renderer_name: None, - clipboard_ctx: None, + clipboard_ctx: Box::new(ClipboardContext::dummy().into()), } } fn is_current_context(&self) -> bool { @@ -317,7 +321,7 @@ impl SuspendedContext { log_filename: None, platform_name: None, renderer_name: None, - clipboard_ctx: None, + clipboard_ctx: Box::new(ClipboardContext::dummy().into()), }; if ctx.is_current_context() { // Oops, the context was activated -> deactivate diff --git a/imgui/src/input_widget.rs b/imgui/src/input_widget.rs index 7ceed36..9c99169 100644 --- a/imgui/src/input_widget.rs +++ b/imgui/src/input_widget.rs @@ -208,8 +208,14 @@ where /// /// If, for some reason, you don't want this, you can run this function to prevent this. /// In that case, edits which would cause a resize will not occur. + /// + /// # Safety + /// Importantly, we silently push and pop a `\0` to the string given here. + /// If you do not want mutable access (ie, do not want that string to resize), + /// you **must** make sure to null-terminate it yourself. This is janky, but ImGui + /// expects a null termination, and we didn't want to re-allocate an entire string per call. #[inline] - pub fn do_not_resize(mut self) -> Self { + pub unsafe fn do_not_resize(mut self) -> Self { self.flags.remove(InputTextFlags::CALLBACK_RESIZE); self } @@ -246,6 +252,8 @@ where } pub fn build(self) -> bool { + // needs to be null-terminated! + self.buf.push('\0'); let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { @@ -254,7 +262,7 @@ where }; let data = &mut data as *mut _ as *mut c_void; - unsafe { + let o = unsafe { if let Some(hint) = self.hint { let (label, hint) = self.ui.scratch_txt_two(self.label, hint); sys::igInputTextWithHint( @@ -278,7 +286,14 @@ where data, ) } + }; + + // it should always end with this \0. + if self.buf.ends_with('\0') { + self.buf.pop(); } + + o } } @@ -349,6 +364,8 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef> InputTextMultiline<'ui } pub fn build(self) -> bool { + // needs to be null-terminated! + self.buf.push('\0'); let (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity()); let mut data = UserData { @@ -357,7 +374,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef> InputTextMultiline<'ui }; let data = &mut data as *mut _ as *mut c_void; - unsafe { + let o = unsafe { sys::igInputTextMultiline( self.ui.scratch_txt(self.label), ptr as *mut sys::cty::c_char, @@ -367,7 +384,14 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef> InputTextMultiline<'ui Some(callback::), data, ) + }; + + // it should always end with this \0. + if self.buf.ends_with('\0') { + self.buf.pop(); } + + o } } @@ -857,17 +881,24 @@ extern "C" fn callback( } InputTextFlags::CALLBACK_RESIZE => { unsafe { - let requested_size = (*data).BufSize as usize; - let buffer = &mut callback_data.user_data.container; - todo!() - // if requested_size > buffer.capacity_with_nul() { - // // Refresh the buffer's length to take into account changes made by dear imgui. - // buffer.refresh_len(); - // buffer.reserve(requested_size - buffer.0.len()); - // debug_assert!(buffer.capacity_with_nul() >= requested_size); - // (*data).Buf = buffer.as_mut_ptr(); - // (*data).BufDirty = true; - // } + let requested_size = (*data).BufTextLen as usize; + + // just confirm that we ARE working with our string. + debug_assert_eq!( + callback_data.user_data.container.as_ptr() as *const _, + (*data).Buf + ); + + if requested_size > callback_data.user_data.container.capacity() { + // reserve more data... + callback_data + .user_data + .container + .reserve(requested_size - callback_data.user_data.container.capacity()); + + (*data).Buf = callback_data.user_data.container.as_mut_ptr() as *mut _; + (*data).BufDirty = true; + } } } InputTextFlags::CALLBACK_CHAR_FILTER => { diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 581f701..9ed9e24 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -438,18 +438,27 @@ impl<'ui> Ui<'ui> { // Widgets: ListBox impl<'ui> Ui<'ui> { #[doc(alias = "ListBox")] - pub fn list_box<'p, StringType: AsRef + ?Sized>( + pub fn list_box<'p, StringType: AsRef + ?Sized>( &self, - label: &'p ImStr, + label: impl AsRef, current_item: &mut i32, items: &'p [&'p StringType], height_in_items: i32, ) -> bool { - let items_inner: Vec<*const c_char> = - items.iter().map(|item| item.as_ref().as_ptr()).collect(); + let (label_ptr, items_inner) = unsafe { + let handle = &mut *self.buffer.get(); + + handle.refresh_buffer(); + let label_ptr = handle.push(label); + + let items_inner: Vec<_> = items.iter().map(|&v| handle.push(v)).collect(); + + (label_ptr, items_inner) + }; + unsafe { sys::igListBoxStr_arr( - label.as_ptr(), + label_ptr, current_item, items_inner.as_ptr() as *mut *const c_char, items_inner.len() as i32, @@ -457,11 +466,50 @@ impl<'ui> Ui<'ui> { ) } } + + // written out for the future times... + // #[doc(alias = "ListBox")] + // pub fn list_box_const<'p, StringType: AsRef + ?Sized, const N: usize>( + // &self, + // label: impl AsRef, + // current_item: &mut i32, + // items: [&'p StringType; N], + // height_in_items: i32, + // ) -> bool { + // let (label_ptr, items_inner) = unsafe { + // let handle = &mut *self.buffer.get(); + + // handle.refresh_buffer(); + // let label_ptr = handle.push(label); + + // let mut items_inner: [*const i8; N] = [std::ptr::null(); N]; + + // for (i, item) in items.iter().enumerate() { + // items_inner[i] = handle.push(item); + // } + + // (label_ptr, items_inner) + // }; + + // unsafe { + // sys::igListBoxStr_arr( + // label_ptr, + // current_item, + // items_inner.as_ptr() as *mut *const c_char, + // items_inner.len() as i32, + // height_in_items, + // ) + // } + // } } impl<'ui> Ui<'ui> { #[doc(alias = "PlotLines")] - pub fn plot_lines<'p>(&self, label: &'p ImStr, values: &'p [f32]) -> PlotLines<'ui, 'p> { + pub fn plot_lines<'p, Label: AsRef>( + &'ui self, + label: Label, + values: &'p [f32], + ) -> PlotLines<'ui, 'p, Label> { PlotLines::new(self, label, values) } } diff --git a/imgui/src/plotlines.rs b/imgui/src/plotlines.rs index 9f7f4c1..2390b3e 100644 --- a/imgui/src/plotlines.rs +++ b/imgui/src/plotlines.rs @@ -1,23 +1,22 @@ -use std::marker::PhantomData; use std::os::raw::c_float; -use std::{f32, mem, ptr}; +use std::{f32, mem}; -use super::{ImStr, Ui}; +use super::Ui; #[must_use] -pub struct PlotLines<'ui, 'p> { - label: &'p ImStr, +pub struct PlotLines<'ui, 'p, Label, Overlay = &'static str> { + label: Label, values: &'p [f32], values_offset: usize, - overlay_text: Option<&'p ImStr>, + overlay_text: Option, scale_min: f32, scale_max: f32, graph_size: [f32; 2], - _phantom: PhantomData<&'ui Ui<'ui>>, + ui: &'ui Ui<'ui>, } -impl<'ui, 'p> PlotLines<'ui, 'p> { - pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { +impl<'ui, 'p, Label: AsRef> PlotLines<'ui, 'p, Label> { + pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self { PlotLines { label, values, @@ -26,48 +25,58 @@ impl<'ui, 'p> PlotLines<'ui, 'p> { scale_min: f32::MAX, scale_max: f32::MAX, graph_size: [0.0, 0.0], - _phantom: PhantomData, + ui, } } +} - #[inline] - pub const fn values_offset(mut self, values_offset: usize) -> Self { +impl<'ui, 'p, Label: AsRef, Overlay: AsRef> PlotLines<'ui, 'p, Label, Overlay> { + pub fn values_offset(mut self, values_offset: usize) -> Self { self.values_offset = values_offset; self } - #[inline] - pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { - self.overlay_text = Some(overlay_text); - self + pub fn overlay_text>( + self, + overlay_text: Overlay2, + ) -> PlotLines<'ui, 'p, Label, Overlay2> { + PlotLines { + label: self.label, + values: self.values, + values_offset: self.values_offset, + overlay_text: Some(overlay_text), + scale_min: self.scale_min, + scale_max: self.scale_max, + graph_size: self.graph_size, + ui: self.ui, + } } - #[inline] - pub const fn scale_min(mut self, scale_min: f32) -> Self { + pub fn scale_min(mut self, scale_min: f32) -> Self { self.scale_min = scale_min; self } - #[inline] - pub const fn scale_max(mut self, scale_max: f32) -> Self { + pub fn scale_max(mut self, scale_max: f32) -> Self { self.scale_max = scale_max; self } - #[inline] - pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self { + pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self { self.graph_size = graph_size; self } pub fn build(self) { unsafe { + let (label, overlay) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text); + sys::igPlotLinesFloatPtr( - self.label.as_ptr(), + label, self.values.as_ptr() as *const c_float, self.values.len() as i32, self.values_offset as i32, - self.overlay_text.map(|x| x.as_ptr()).unwrap_or(ptr::null()), + overlay, self.scale_min, self.scale_max, self.graph_size.into(), diff --git a/imgui/src/popups.rs b/imgui/src/popups.rs index ca30b10..dc6f9aa 100644 --- a/imgui/src/popups.rs +++ b/imgui/src/popups.rs @@ -2,16 +2,7 @@ use std::ptr; use crate::sys; use crate::window::WindowFlags; -use crate::{ImStr, Ui}; - -create_token!( - /// Tracks a popup token that can be ended with `end` or by dropping. - pub struct PopupToken<'ui>; - - /// Drops the popup token manually. You can also just allow this token - /// to drop on its own. - drop { sys::igEndPopup() } -); +use crate::Ui; /// Create a modal pop-up. /// @@ -31,14 +22,14 @@ create_token!( /// }; /// ``` #[must_use] -pub struct PopupModal<'p> { - label: &'p ImStr, +pub struct PopupModal<'p, Label> { + label: Label, opened: Option<&'p mut bool>, flags: WindowFlags, } -impl<'p> PopupModal<'p> { - pub fn new(label: &'p ImStr) -> Self { +impl<'p, Label: AsRef> PopupModal<'p, Label> { + pub fn new(label: Label) -> Self { PopupModal { label, opened: None, @@ -140,7 +131,7 @@ impl<'p> PopupModal<'p> { pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option> { let render = unsafe { sys::igBeginPopupModal( - self.label.as_ptr(), + ui.scratch_txt(self.label), self.opened .map(|x| x as *mut bool) .unwrap_or(ptr::null_mut()), @@ -165,8 +156,8 @@ impl<'ui> Ui<'ui> { /// can also force close a popup when a user clicks outside a popup. If you do not want users to be /// able to close a popup without selected an option, use [`PopupModal`]. #[doc(alias = "OpenPopup")] - pub fn open_popup(&self, str_id: &ImStr) { - unsafe { sys::igOpenPopup(str_id.as_ptr(), 0) }; + pub fn open_popup(&self, str_id: impl AsRef) { + unsafe { sys::igOpenPopup(self.scratch_txt(str_id), 0) }; } /// Construct a popup that can have any kind of content. @@ -174,9 +165,10 @@ impl<'ui> Ui<'ui> { /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once* /// when you want to actual create the popup. #[doc(alias = "BeginPopup")] - pub fn begin_popup(&self, str_id: &ImStr) -> Option> { - let render = - unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) }; + pub fn begin_popup(&self, str_id: impl AsRef) -> Option> { + let render = unsafe { + sys::igBeginPopup(self.scratch_txt(str_id), WindowFlags::empty().bits() as i32) + }; if render { Some(PopupToken::new(self)) @@ -190,21 +182,17 @@ impl<'ui> Ui<'ui> { /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once* /// when you want to actual create the popup. #[doc(alias = "BeginPopup")] - pub fn popup(&self, str_id: &ImStr, f: F) + pub fn popup(&self, str_id: impl AsRef, f: F) where F: FnOnce(), { - let render = - unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) }; - if render { + if let Some(_t) = self.begin_popup(str_id) { f(); - unsafe { sys::igEndPopup() }; } } /// Creates a PopupModal directly. - #[deprecated = "Please use PopupModal to create a modal popup."] - pub fn popup_modal<'p>(&self, str_id: &'p ImStr) -> PopupModal<'p> { + pub fn popup_modal<'p, Label: AsRef>(&self, str_id: Label) -> PopupModal<'p, Label> { PopupModal::new(str_id) } @@ -215,3 +203,12 @@ impl<'ui> Ui<'ui> { unsafe { sys::igCloseCurrentPopup() }; } } + +create_token!( + /// Tracks a popup token that can be ended with `end` or by dropping. + pub struct PopupToken<'ui>; + + /// Drops the popup token manually. You can also just allow this token + /// to drop on its own. + drop { sys::igEndPopup() } +); diff --git a/imgui/src/string.rs b/imgui/src/string.rs index a345ce7..98e3297 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -74,6 +74,7 @@ impl UiBuffer { } #[macro_export] +#[deprecated = "all functions take AsRef now -- use inline strings or `format` instead"] macro_rules! im_str { ($e:literal $(,)?) => {{ const __INPUT: &str = concat!($e, "\0"); @@ -345,7 +346,6 @@ impl fmt::Write for ImString { /// A UTF-8 encoded, implicitly nul-terminated string slice. #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -#[deprecated] pub struct ImStr([u8]); impl<'a> Default for &'a ImStr { From 7bc3715f42259fc9a7d23abfa97ee04d00c86c7e Mon Sep 17 00:00:00 2001 From: Jack Mac Date: Sun, 12 Sep 2021 02:24:08 -0400 Subject: [PATCH 11/21] 1.48 required T: Debug for unsafe cell's Debug impl --- imgui/src/string.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui/src/string.rs b/imgui/src/string.rs index 98e3297..1ba181b 100644 --- a/imgui/src/string.rs +++ b/imgui/src/string.rs @@ -6,6 +6,7 @@ use std::str; use std::{fmt, ptr}; /// this is the unsafe cell upon which we build our abstraction. +#[derive(Debug)] pub(crate) struct UiBuffer { buffer: Vec, max_len: usize, From 2a547f2a665d8984a936ae40ea9ded2936f72229 Mon Sep 17 00:00:00 2001 From: Jack Mac Date: Sun, 12 Sep 2021 02:33:16 -0400 Subject: [PATCH 12/21] updated changelog to ignore combobox message. also removed the previous notion that some things required &str -- we figured out how to not need that --- CHANGELOG.markdown | 5 +++++ imgui/src/widget/combo_box.rs | 9 --------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index ec23e4e..bb3564a 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -4,6 +4,11 @@ - BREAKING: MSRV is now **1.54**. This is gives us access to min-const-generics, which we use in a few places, but will gradually use more. Because this is the first time we've bumped MSRV intentionally, we have added a new feature `min-const-generics`, which is _enabled by default_. If you are pre-1.54, you can hang onto this update by disabling that feature. In our next update, this feature will be removed and we will commit to our MSRVs going forward. +- **Removed ImStr and ImString from the API.** Currently `im_str!` is deprecated and **will be removed in 0.9.0**. To change your code: + + - If you were just wrapping a string literal, like `im_str!("button")`, just use `"button"`. + - If you were formatting, like `&im_str!("My age is {}", 100)`, you can now just use format like `format!("My age is {}, 100)`. Notice that due to the trait bounds, you can pass the string in directly too. + - Removed automatically adding default features for `imgui-winit-support` with the exception of the current default winit feature/dep version. Additionally, that version was updated to 0.25. If you want to not have the default features of winit with 0.25, set `default-features = false` and add `winit-25` as a normal feature. Thank you to @dzil123 for the work [implementing this here](https://github.com/imgui-rs/imgui-rs/pull/477)! diff --git a/imgui/src/widget/combo_box.rs b/imgui/src/widget/combo_box.rs index 4debebf..3d37859 100644 --- a/imgui/src/widget/combo_box.rs +++ b/imgui/src/widget/combo_box.rs @@ -296,13 +296,4 @@ impl<'ui> Ui<'ui> { ) -> bool { self.combo(label, current_item, items, |s| Cow::Borrowed(s.as_ref())) } - - // /// Builds a simple combo box for choosing from a slice of strings - // #[doc(alias = "BeginCombo")] - // pub fn build_simple_string(self, ui: &Ui, current_item: &mut usize, items: &[&S]) -> bool - // where - // S: AsRef + ?Sized, - // { - // self.build_simple(ui, current_item, items, &|&s| s.as_ref().into()) - // } } From f635fd6438ee7442d1b1b94868f824eb0bb2569a Mon Sep 17 00:00:00 2001 From: Jack Mac Date: Sun, 12 Sep 2021 17:16:53 -0400 Subject: [PATCH 13/21] fixed examples, removing warnings (i think) --- imgui-examples/examples/collapsing_header.rs | 16 +- imgui-examples/examples/color_button.rs | 73 ++- imgui-examples/examples/custom_textures.rs | 10 +- imgui-examples/examples/draw_list.rs | 12 +- imgui-examples/examples/hello_world.rs | 4 +- imgui-examples/examples/keyboard.rs | 6 +- imgui-examples/examples/long_list.rs | 2 +- imgui-examples/examples/multiple_fonts.rs | 2 +- imgui-examples/examples/progress_bar.rs | 2 +- imgui-examples/examples/radio_button.rs | 44 +- imgui-examples/examples/slider.rs | 24 +- imgui-examples/examples/test_window_impl.rs | 504 +++++++++--------- imgui-examples/examples/text_callbacks.rs | 2 +- .../examples/gfx_custom_textures.rs | 4 +- .../examples/gfx_hello_world.rs | 10 +- .../examples/04_custom_textures.rs | 12 +- imgui/src/widget/menu.rs | 26 +- imgui/src/widget/slider.rs | 140 +++-- 18 files changed, 468 insertions(+), 425 deletions(-) diff --git a/imgui-examples/examples/collapsing_header.rs b/imgui-examples/examples/collapsing_header.rs index 8c4e7c4..74873d7 100644 --- a/imgui-examples/examples/collapsing_header.rs +++ b/imgui-examples/examples/collapsing_header.rs @@ -8,19 +8,19 @@ fn main() { }; let system = support::init(file!()); system.main_loop(move |run, ui| { - let w = Window::new(im_str!("Collapsing header")) + let w = Window::new("Collapsing header") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 500.0], Condition::Appearing); w.build(ui, || { - if CollapsingHeader::new(im_str!("I'm a collapsing header. Click me!")).build(ui) { + if CollapsingHeader::new("I'm a collapsing header. Click me!").build(ui) { ui.text( "A collapsing header can be used to toggle rendering of a group of widgets", ); } ui.spacing(); - if CollapsingHeader::new(im_str!("I'm open by default")) + if CollapsingHeader::new("I'm open by default") .default_open(true) .build(ui) { @@ -28,7 +28,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I only open with double-click")) + if CollapsingHeader::new("I only open with double-click") .open_on_double_click(true) .build(ui) { @@ -36,7 +36,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I don't have an arrow")) + if CollapsingHeader::new("I don't have an arrow") .bullet(true) .build(ui) { @@ -44,7 +44,7 @@ fn main() { } ui.spacing(); - if CollapsingHeader::new(im_str!("I only open if you click the arrow")) + if CollapsingHeader::new("I only open if you click the arrow") .open_on_arrow(true) .build(ui) { @@ -53,10 +53,10 @@ fn main() { ui.spacing(); ui.checkbox( - im_str!("Toggle rendering of the next example"), + "Toggle rendering of the next example", &mut state.render_closable, ); - if CollapsingHeader::new(im_str!("I've got a separate close button")) + if CollapsingHeader::new("I've got a separate close button") .build_with_close_button(ui, &mut state.render_closable) { ui.text("I've got contents just like any other collapsing header"); diff --git a/imgui-examples/examples/color_button.rs b/imgui-examples/examples/color_button.rs index 390b89d..3c48aab 100644 --- a/imgui-examples/examples/color_button.rs +++ b/imgui-examples/examples/color_button.rs @@ -17,15 +17,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Color button examples")) + let w = Window::new("Color button examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 100.0], Condition::Appearing) .resizable(false); w.build(ui, || { - let ex1 = ui.radio_button(im_str!("Example 1: Basics"), &mut state.example, 1); - let ex2 = ui.radio_button(im_str!("Example 2: Alpha component"), &mut state.example, 2); - let ex3 = ui.radio_button(im_str!("Example 3: Input format"), &mut state.example, 3); + let ex1 = ui.radio_button("Example 1: Basics", &mut state.example, 1); + let ex2 = ui.radio_button("Example 2: Alpha component", &mut state.example, 2); + let ex3 = ui.radio_button("Example 3: Input format", &mut state.example, 3); if ex1 || ex2 || ex3 { state.reset(); } @@ -33,29 +33,29 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Basics")) + let w = Window::new("Example 1: Basics") .size([700.0, 300.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Color button is a widget that displays a color value as a clickable rectangle. \ It also supports a tooltip with detailed information about the color value. \ - Try hovering over and clicking these buttons!" - )); + Try hovering over and clicking these buttons!", + ); ui.text(state.notify_text); ui.text("This button is black:"); - if ColorButton::new(im_str!("Black color"), [0.0, 0.0, 0.0, 1.0]).build(ui) { + if ColorButton::new("Black color", [0.0, 0.0, 0.0, 1.0]).build(ui) { state.notify_text = "*** Black button was clicked"; } ui.text("This button is red:"); - if ColorButton::new(im_str!("Red color"), [1.0, 0.0, 0.0, 1.0]).build(ui) { + if ColorButton::new("Red color", [1.0, 0.0, 0.0, 1.0]).build(ui) { state.notify_text = "*** Red button was clicked"; } ui.text("This button is BIG because it has a custom size:"); - if ColorButton::new(im_str!("Green color"), [0.0, 1.0, 0.0, 1.0]) + if ColorButton::new("Green color", [0.0, 1.0, 0.0, 1.0]) .size([100.0, 50.0]) .build(ui) { @@ -63,7 +63,7 @@ fn example_1(ui: &Ui, state: &mut State) { } ui.text("This button doesn't use the tooltip at all:"); - if ColorButton::new(im_str!("No tooltip"), [0.0, 0.0, 1.0, 1.0]) + if ColorButton::new("No tooltip", [0.0, 0.0, 1.0, 1.0]) .tooltip(false) .build(ui) { @@ -73,71 +73,66 @@ fn example_1(ui: &Ui, state: &mut State) { } fn example_2(ui: &Ui) { - let w = Window::new(im_str!("Example 2: Alpha component")) + let w = Window::new("Example 2: Alpha component") .size([700.0, 320.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "The displayed color is passed to the button as four float values between \ 0.0 - 1.0 (RGBA). If you don't care about the alpha component, it can be \ - disabled and it won't show up in the tooltip" - )); + disabled and it won't show up in the tooltip", + ); ui.text("This button ignores the alpha component:"); - ColorButton::new(im_str!("Red color"), [1.0, 0.0, 0.0, 0.5]) + ColorButton::new("Red color", [1.0, 0.0, 0.0, 0.5]) .alpha(false) .build(ui); ui.spacing(); ui.spacing(); ui.spacing(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "If you *do* care about the alpha component, you can choose how it's \ - displayed in the button and the tooltip" - )); + displayed in the button and the tooltip", + ); ui.separator(); - ui.text_wrapped(im_str!( - "ColorPreview::Opaque (default) doesn't show the alpha component at all" - )); - ColorButton::new(im_str!("Red + ColorPreview::Opaque"), [1.0, 0.0, 0.0, 0.5]) + ui.text_wrapped("ColorPreview::Opaque (default) doesn't show the alpha component at all"); + ColorButton::new("Red + ColorPreview::Opaque", [1.0, 0.0, 0.0, 0.5]) .preview(ColorPreview::Opaque) .build(ui); ui.separator(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "ColorPreview::HalfAlpha divides the color area into two halves and uses a \ - checkerboard pattern in one half to illustrate the alpha component" - )); - ColorButton::new( - im_str!("Red + ColorPreview::HalfAlpha"), - [1.0, 0.0, 0.0, 0.5], - ) - .preview(ColorPreview::HalfAlpha) - .build(ui); + checkerboard pattern in one half to illustrate the alpha component", + ); + ColorButton::new("Red + ColorPreview::HalfAlpha", [1.0, 0.0, 0.0, 0.5]) + .preview(ColorPreview::HalfAlpha) + .build(ui); ui.separator(); - ui.text_wrapped(im_str!( + ui.text_wrapped( "ColorPreview::Alpha uses a checkerboard pattern in the entire color area to \ illustrate the alpha component" - )); - ColorButton::new(im_str!("Red + ColorPreview::Alpha"), [1.0, 0.0, 0.0, 0.5]) + ); + ColorButton::new("Red + ColorPreview::Alpha", [1.0, 0.0, 0.0, 0.5]) .preview(ColorPreview::Alpha) .build(ui); }); } fn example_3(ui: &Ui) { - let w = Window::new(im_str!("Example 3: Input format")) + let w = Window::new("Example 3: Input format") .size([700.0, 320.0], Condition::Appearing) .position([20.0, 140.0], Condition::Appearing); w.build(ui, || { ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as RGB(A) (default):"); - ColorButton::new(im_str!("RGBA red"), [1.0, 0.0, 0.0, 1.0]).build(ui); + ColorButton::new("RGBA red", [1.0, 0.0, 0.0, 1.0]).build(ui); ui.separator(); ui.text("This button interprets the input value [1.0, 0.0, 0.0, 1.0] as HSV(A):"); - ColorButton::new(im_str!("HSVA black"), [1.0, 0.0, 0.0, 1.0]) + ColorButton::new("HSVA black", [1.0, 0.0, 0.0, 1.0]) .input_mode(ColorEditInputMode::HSV) .build(ui); }); diff --git a/imgui-examples/examples/custom_textures.rs b/imgui-examples/examples/custom_textures.rs index c1cce95..a76b03f 100644 --- a/imgui-examples/examples/custom_textures.rs +++ b/imgui-examples/examples/custom_textures.rs @@ -78,10 +78,10 @@ impl CustomTexturesApp { } fn show_textures(&self, ui: &Ui) { - Window::new(im_str!("Hello textures")) + Window::new("Hello textures") .size([400.0, 400.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); if let Some(my_texture_id) = self.my_texture_id { ui.text("Some generated texture"); Image::new(my_texture_id, [100.0, 100.0]).build(ui); @@ -97,7 +97,7 @@ impl CustomTexturesApp { ui.text("The Lenna buttons"); { - ui.invisible_button(im_str!("Boring Button"), [100.0, 100.0]); + ui.invisible_button("Boring Button", [100.0, 100.0]); // See also `imgui::Ui::style_color` let tint_none = [1.0, 1.0, 1.0, 1.0]; let tint_green = [0.5, 1.0, 0.5, 1.0]; @@ -124,7 +124,7 @@ impl CustomTexturesApp { ui.same_line(); // Button using quad positioned image - ui.invisible_button(im_str!("Exciting Button"), [100.0, 100.0]); + ui.invisible_button("Exciting Button", [100.0, 100.0]); // Button bounds let min = ui.item_rect_min(); @@ -151,7 +151,7 @@ impl CustomTexturesApp { // Rounded image { ui.same_line(); - ui.invisible_button(im_str!("Smooth Button"), [100.0, 100.0]); + ui.invisible_button("Smooth Button", [100.0, 100.0]); let draw_list = ui.get_window_draw_list(); draw_list diff --git a/imgui-examples/examples/draw_list.rs b/imgui-examples/examples/draw_list.rs index 9d1a5b2..dfd9125 100644 --- a/imgui-examples/examples/draw_list.rs +++ b/imgui-examples/examples/draw_list.rs @@ -7,7 +7,7 @@ fn draw_text_centered( ui: &Ui, draw_list: &DrawListMut, rect: [f32; 4], - text: &ImStr, + text: &str, color: [f32; 3], ) { let text_size = ui.calc_text_size(text); @@ -37,7 +37,7 @@ fn main() { ui, &bg_draw_list, [0.0, 0.0, 300.0, 300.0], - im_str!("background draw list"), + "background draw list", [0.0, 0.0, 0.0], ); } @@ -52,16 +52,16 @@ fn main() { ui, &fg_draw_list, [w - 300.0, h - 300.0, 300.0, 300.0], - im_str!("foreground draw list"), + "foreground draw list", [1.0, 0.0, 0.0], ); } - Window::new(im_str!("Draw list")) + Window::new("Draw list") .size([300.0, 110.0], Condition::FirstUseEver) .scroll_bar(false) .build(ui, || { - ui.button(im_str!("random button")); + ui.button("random button"); let draw_list = ui.get_window_draw_list(); let o = ui.cursor_screen_pos(); let ws = ui.content_region_avail(); @@ -89,7 +89,7 @@ fn main() { ui, &draw_list, [o[0], o[1], ws[0], ws[1]], - im_str!("window draw list"), + "window draw list", [1.0, 1.0, 1.0], ); }); diff --git a/imgui-examples/examples/hello_world.rs b/imgui-examples/examples/hello_world.rs index 0c87a99..56eb774 100644 --- a/imgui-examples/examples/hello_world.rs +++ b/imgui-examples/examples/hello_world.rs @@ -7,7 +7,7 @@ fn main() { 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")) + Window::new("Hello world") .size([300.0, 110.0], Condition::FirstUseEver) .build(ui, || { ui.text_wrapped("Hello world!"); @@ -16,7 +16,7 @@ fn main() { value += 1; value %= 2; } - + ui.button("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; diff --git a/imgui-examples/examples/keyboard.rs b/imgui-examples/examples/keyboard.rs index 1114c0c..d2d9c3f 100644 --- a/imgui-examples/examples/keyboard.rs +++ b/imgui-examples/examples/keyboard.rs @@ -14,7 +14,7 @@ fn main() { let mut text_buffer = String::new(); system.main_loop(move |_, ui| { - Window::new(im_str!("Means of accessing key state")) + Window::new("Means of accessing key state") .size([500.0, 300.0], Condition::FirstUseEver) .build(ui, || { // You can check if a key is currently held down @@ -81,12 +81,12 @@ fn main() { // example, if you try to type into this input, the // above interaction still counts the key presses. ui.input_text_multiline( - im_str!("##Dummy text input widget"), + "##Dummy text input widget", &mut text_buffer, [100.0, 100.0], ) // .do_not_resize() if you pass this, then this won't resize! - // .hint(im_str!("Example text input")) + // .hint("Example text input") .build(); // If you want to check if a widget is capturing diff --git a/imgui-examples/examples/long_list.rs b/imgui-examples/examples/long_list.rs index 79278af..8a745e6 100644 --- a/imgui-examples/examples/long_list.rs +++ b/imgui-examples/examples/long_list.rs @@ -15,7 +15,7 @@ fn main() { let system = support::init(file!()); system.main_loop(move |_, ui| { - Window::new(im_str!("Hello long world")) + Window::new("Hello long world") .size([300.0, 110.0], Condition::FirstUseEver) .build(ui, || { let mut clipper = imgui::ListClipper::new(lots_of_words.len() as i32) diff --git a/imgui-examples/examples/multiple_fonts.rs b/imgui-examples/examples/multiple_fonts.rs index e78a17b..8517593 100644 --- a/imgui-examples/examples/multiple_fonts.rs +++ b/imgui-examples/examples/multiple_fonts.rs @@ -19,7 +19,7 @@ fn main() { .reload_font_texture(&mut system.imgui) .expect("Failed to reload fonts"); system.main_loop(move |run, ui| { - Window::new(im_str!("Hello world")) + Window::new("Hello world") .opened(run) .build(ui, || { ui.text("Hello, I'm the default font!"); diff --git a/imgui-examples/examples/progress_bar.rs b/imgui-examples/examples/progress_bar.rs index 65cbcd5..2419ae8 100644 --- a/imgui-examples/examples/progress_bar.rs +++ b/imgui-examples/examples/progress_bar.rs @@ -5,7 +5,7 @@ mod support; fn main() { let system = support::init(file!()); system.main_loop(move |run, ui| { - let w = Window::new(im_str!("Progress bar")) + let w = Window::new("Progress bar") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 200.0], Condition::Appearing); diff --git a/imgui-examples/examples/radio_button.rs b/imgui-examples/examples/radio_button.rs index d968c1d..cb86a82 100644 --- a/imgui-examples/examples/radio_button.rs +++ b/imgui-examples/examples/radio_button.rs @@ -16,19 +16,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Radio button examples")) + let w = Window::new("Radio button examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 80.0], Condition::Appearing) .resizable(false); w.build(ui, || { let mut clicked = false; - clicked |= ui.radio_button( - im_str!("Example 1: Boolean radio buttons"), - &mut state.example, - 1, - ); - clicked |= ui.radio_button(im_str!("Example 2: Radio buttons"), &mut state.example, 2); + clicked |= ui.radio_button("Example 1: Boolean radio buttons", &mut state.example, 1); + clicked |= ui.radio_button("Example 2: Radio buttons", &mut state.example, 2); if clicked { state.reset(); } @@ -36,25 +32,25 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Boolean radio buttons")) + let w = Window::new("Example 1: Boolean radio buttons") .size([700.0, 200.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Boolean radio buttons accept a boolean active state, which is passed as a value and \ not as a mutable reference. This means that it's not updated automatically, so you \ can implement any click behaviour you want. The return value is true if the button \ - was clicked." - )); + was clicked.", + ); ui.text(state.notify_text); - if ui.radio_button_bool(im_str!("I'm permanently active"), true) { + if ui.radio_button_bool("I'm permanently active", true) { state.notify_text = "*** Permanently active radio button was clicked"; } - if ui.radio_button_bool(im_str!("I'm permanently inactive"), false) { + if ui.radio_button_bool("I'm permanently inactive", false) { state.notify_text = "*** Permanently inactive radio button was clicked"; } - if ui.radio_button_bool(im_str!("I toggle my state on click"), state.simple_bool) { + if ui.radio_button_bool("I toggle my state on click", state.simple_bool) { state.simple_bool = !state.simple_bool; // flip state on click state.notify_text = "*** Toggling radio button was clicked"; } @@ -62,36 +58,36 @@ fn example_1(ui: &Ui, state: &mut State) { } fn example_2(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 2: Radio buttons")) + let w = Window::new("Example 2: Radio buttons") .size([700.0, 300.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { - ui.text_wrapped(im_str!( + ui.text_wrapped( "Normal radio buttons accept a mutable reference to state, and the value \ corresponding to this button. They are very flexible, because the value can be any \ - type that is both Copy and PartialEq. This is especially useful with Rust enums" - )); + type that is both Copy and PartialEq. This is especially useful with Rust enums", + ); ui.text(state.notify_text); ui.separator(); - if ui.radio_button(im_str!("I'm number 1"), &mut state.number, 1) { + if ui.radio_button("I'm number 1", &mut state.number, 1) { state.notify_text = "*** Number 1 was clicked"; } - if ui.radio_button(im_str!("I'm number 2"), &mut state.number, 2) { + if ui.radio_button("I'm number 2", &mut state.number, 2) { state.notify_text = "*** Number 2 was clicked"; } - if ui.radio_button(im_str!("I'm number 3"), &mut state.number, 3) { + if ui.radio_button("I'm number 3", &mut state.number, 3) { state.notify_text = "*** Number 3 was clicked"; } ui.separator(); - if ui.radio_button(im_str!("I'm choice A"), &mut state.choice, Some(Choice::A)) { + if ui.radio_button("I'm choice A", &mut state.choice, Some(Choice::A)) { state.notify_text = "*** Choice A was clicked"; } - if ui.radio_button(im_str!("I'm choice B"), &mut state.choice, Some(Choice::B)) { + if ui.radio_button("I'm choice B", &mut state.choice, Some(Choice::B)) { state.notify_text = "*** Choice B was clicked"; } - if ui.radio_button(im_str!("I'm choice C"), &mut state.choice, Some(Choice::C)) { + if ui.radio_button("I'm choice C", &mut state.choice, Some(Choice::C)) { state.notify_text = "*** Choice C was clicked"; } }); diff --git a/imgui-examples/examples/slider.rs b/imgui-examples/examples/slider.rs index 00c1428..e5e5bcc 100644 --- a/imgui-examples/examples/slider.rs +++ b/imgui-examples/examples/slider.rs @@ -16,15 +16,15 @@ fn main() { } fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { - let w = Window::new(im_str!("Slider examples")) + let w = Window::new("Slider examples") .opened(run) .position([20.0, 20.0], Condition::Appearing) .size([700.0, 80.0], Condition::Appearing) .resizable(false); w.build(ui, || { let mut clicked = false; - clicked |= ui.radio_button(im_str!("Example 1: Basic sliders"), &mut state.example, 1); - clicked |= ui.radio_button(im_str!("Example 2: Slider arrays"), &mut state.example, 2); + clicked |= ui.radio_button("Example 1: Basic sliders", &mut state.example, 1); + clicked |= ui.radio_button("Example 2: Slider arrays", &mut state.example, 2); if clicked { state.reset(); } @@ -32,7 +32,7 @@ fn example_selector(run: &mut bool, ui: &mut Ui, state: &mut State) { } fn example_1(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 1: Basic sliders")) + let w = Window::new("Example 1: Basic sliders") .size([700.0, 340.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { @@ -41,42 +41,42 @@ fn example_1(ui: &Ui, state: &mut State) { ui.text("Unsigned: u8 u16 u32 u64"); ui.text("Floats: f32 f64"); - Slider::new(im_str!("u8 value"), 0, 255) + Slider::new("u8 value", 0, 255) .build(ui, &mut state.u8_value); - Slider::new(im_str!("f32 value"), -f32::MIN, f32::MAX) + Slider::new("f32 value", -f32::MIN, f32::MAX) .build(ui, &mut state.f32_value); ui.separator(); ui.text("Slider range can be limited:"); - Slider::new(im_str!("i32 value with range"), -999, 999) + Slider::new("i32 value with range", -999, 999) .build(ui, &mut state.i32_value); ui.text("Note that for 32-bit/64-bit types, sliders are always limited to half of the natural type range!"); ui.separator(); ui.text("Value formatting can be customized with a C-style printf string:"); - Slider::new(im_str!("f64 value with custom formatting"), -999_999_999.0, 999_999_999.0) + Slider::new("f64 value with custom formatting", -999_999_999.0, 999_999_999.0) .display_format("%09.0f") .build(ui, &mut state.f64_formatted); ui.separator(); ui.text("Vertical sliders require a size parameter but otherwise work in a similar way:"); - VerticalSlider::new(im_str!("vertical\nu8 value"), [50.0, 50.0], u8::MIN, u8::MAX) + VerticalSlider::new("vertical\nu8 value", [50.0, 50.0], u8::MIN, u8::MAX) .build(ui, &mut state.u8_value); }); } fn example_2(ui: &Ui, state: &mut State) { - let w = Window::new(im_str!("Example 2: Slider arrays")) + let w = Window::new("Example 2: Slider arrays") .size([700.0, 260.0], Condition::Appearing) .position([20.0, 120.0], Condition::Appearing); w.build(ui, || { ui.text("You can easily build a slider group from an array of values:"); - Slider::new(im_str!("[u8; 4]"), 0, u8::MAX).build_array(ui, &mut state.array); + Slider::new("[u8; 4]", 0, u8::MAX).build_array(ui, &mut state.array); ui.text("You don't need to use arrays with known length; arbitrary slices can be used:"); let slice: &mut [u8] = &mut state.array[1..=2]; - Slider::new(im_str!("subslice"), 0, u8::MAX).build_array(ui, slice); + Slider::new("subslice", 0, u8::MAX).build_array(ui, slice); }); } diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index 29e1acf..f47c4a2 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -236,7 +236,7 @@ fn main() { } fn show_help_marker(ui: &Ui, desc: &str) { - ui.text_disabled(im_str!("(?)")); + ui.text_disabled("(?)"); if ui.is_item_hovered() { ui.tooltip(|| { ui.text(desc); @@ -245,18 +245,14 @@ fn show_help_marker(ui: &Ui, desc: &str) { } fn show_user_guide(ui: &Ui) { - ui.bullet_text(im_str!("Double-click on title bar to collapse window.")); - ui.bullet_text(im_str!( - "Click and drag on lower right corner to resize window." - )); - ui.bullet_text(im_str!("Click and drag on any empty space to move window.")); - ui.bullet_text(im_str!("Mouse Wheel to scroll.")); + ui.bullet_text("Double-click on title bar to collapse window."); + ui.bullet_text("Click and drag on lower right corner to resize window."); + ui.bullet_text("Click and drag on any empty space to move window."); + ui.bullet_text("Mouse Wheel to scroll."); // TODO: check font_allow_user_scaling - ui.bullet_text(im_str!( - "TAB/SHIFT+TAB to cycle through keyboard editable fields." - )); - ui.bullet_text(im_str!("CTRL+Click on a slider or drag box to input text.")); - ui.bullet_text(im_str!( + ui.bullet_text("TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ui.bullet_text("CTRL+Click on a slider or drag box to input text."); + ui.bullet_text( "While editing text: - Hold SHIFT or use mouse to select text - CTRL+Left/Right to word jump @@ -265,8 +261,8 @@ fn show_user_guide(ui: &Ui) { - CTRL+Z,CTRL+Y undo/redo - ESCAPE to revert - You can apply arithmetic operators +,*,/ on numerical values. - Use +- to subtract.\n" - )); + Use +- to subtract.\n", + ); } fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { @@ -290,12 +286,12 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.show_metrics_window(&mut state.show_app_metrics); } if state.show_app_style_editor { - Window::new(im_str!("Style Editor")) + Window::new("Style Editor") .opened(&mut state.show_app_style_editor) .build(ui, || ui.show_default_style_editor()); } if state.show_app_about { - Window::new(im_str!("About ImGui")) + Window::new("About ImGui") .always_auto_resize(true) .opened(&mut state.show_app_about) .build(ui, || { @@ -321,7 +317,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { show_app_log(ui, &mut state.app_log); } - let mut window = Window::new(im_str!("ImGui Demo")) + let mut window = Window::new("ImGui Demo") .title_bar(!state.no_titlebar) .resizable(!state.no_resize) .movable(!state.no_move) @@ -336,182 +332,182 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { ui.push_item_width(-140.0); ui.text(format!("dear imgui says hello. ({})", imgui::dear_imgui_version())); if let Some(menu_bar) = ui.begin_menu_bar() { - if let Some(menu) = ui.begin_menu(im_str!("Menu")) { + if let Some(menu) = ui.begin_menu("Menu") { show_example_menu_file(ui, &mut state.file_menu); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Examples")) { - MenuItem::new(im_str!("Main menu bar")) + if let Some(menu) = ui.begin_menu("Examples") { + MenuItem::new("Main menu bar") .build_with_ref(ui, &mut state.show_app_main_menu_bar); - MenuItem::new(im_str!("Console")) + MenuItem::new("Console") .build_with_ref(ui, &mut state.show_app_console); - MenuItem::new(im_str!("Log")) + MenuItem::new("Log") .build_with_ref(ui, &mut state.show_app_log); - MenuItem::new(im_str!("Simple layout")) + MenuItem::new("Simple layout") .build_with_ref(ui, &mut state.show_app_layout); - MenuItem::new(im_str!("Property editor")) + MenuItem::new("Property editor") .build_with_ref(ui, &mut state.show_app_property_editor); - MenuItem::new(im_str!("Long text display")) + MenuItem::new("Long text display") .build_with_ref(ui, &mut state.show_app_long_text); - MenuItem::new(im_str!("Auto-resizing window")) + MenuItem::new("Auto-resizing window") .build_with_ref(ui, &mut state.show_app_auto_resize); - MenuItem::new(im_str!("Constrained-resizing window")) + MenuItem::new("Constrained-resizing window") .build_with_ref(ui, &mut state.show_app_constrained_resize); - MenuItem::new(im_str!("Simple overlay")) + MenuItem::new("Simple overlay") .build_with_ref(ui, &mut state.show_app_fixed_overlay); - MenuItem::new(im_str!("Manipulating window title")) + MenuItem::new("Manipulating window title") .build_with_ref(ui, &mut state.show_app_manipulating_window_title); - MenuItem::new(im_str!("Custom rendering")) + MenuItem::new("Custom rendering") .build_with_ref(ui, &mut state.show_app_custom_rendering); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Help")) { - MenuItem::new(im_str!("Metrics")) + if let Some(menu) = ui.begin_menu("Help") { + MenuItem::new("Metrics") .build_with_ref(ui, &mut state.show_app_metrics); - MenuItem::new(im_str!("Style Editor")) + MenuItem::new("Style Editor") .build_with_ref(ui, &mut state.show_app_style_editor); - MenuItem::new(im_str!("About ImGui")) + MenuItem::new("About ImGui") .build_with_ref(ui, &mut state.show_app_about); menu.end(); } menu_bar.end(); } ui.spacing(); - if CollapsingHeader::new(im_str!("Help")).build(ui) { - ui.text_wrapped(im_str!( + if CollapsingHeader::new("Help").build(ui) { + ui.text_wrapped( "This window is being created by the show_test_window() \ function. Please refer to the code for programming \ reference.\n\nUser Guide:" - )); + ); show_user_guide(ui); } - if CollapsingHeader::new(im_str!("Window options")).build(ui) { - ui.checkbox(im_str!("No titlebar"), &mut state.no_titlebar); + if CollapsingHeader::new("Window options").build(ui) { + ui.checkbox("No titlebar", &mut state.no_titlebar); ui.same_line_with_pos(150.0); - ui.checkbox(im_str!("No scrollbar"), &mut state.no_scrollbar); + ui.checkbox("No scrollbar", &mut state.no_scrollbar); ui.same_line_with_pos(300.0); - ui.checkbox(im_str!("No menu"), &mut state.no_menu); - ui.checkbox(im_str!("No move"), &mut state.no_move); + ui.checkbox("No menu", &mut state.no_menu); + ui.checkbox("No move", &mut state.no_move); ui.same_line_with_pos(150.0); - ui.checkbox(im_str!("No resize"), &mut state.no_resize); + ui.checkbox("No resize", &mut state.no_resize); ui.same_line_with_pos(300.0); - ui.checkbox(im_str!("No collapse"), &mut state.no_collapse); - ui.checkbox(im_str!("No close"), &mut state.no_close); + ui.checkbox("No collapse", &mut state.no_collapse); + ui.checkbox("No close", &mut state.no_close); - TreeNode::new(im_str!("Style")).build(ui, || { + TreeNode::new("Style").build(ui, || { ui.show_default_style_editor(); }); } - if CollapsingHeader::new(im_str!("Widgets")).build(ui) { - TreeNode::new(im_str!("Tree")).build(ui, || { + if CollapsingHeader::new("Widgets").build(ui) { + TreeNode::new("Tree").build(ui, || { for i in 0..5 { - TreeNode::new(&im_str!("Child {}", i)).build(ui, || { - ui.text(im_str!("blah blah")); + TreeNode::new(format!("Child {}", i)).build(ui, || { + ui.text("blah blah"); ui.same_line(); - if ui.small_button(im_str!("print")) { + if ui.small_button("print") { println!("Child {} pressed", i); } }); } }); - TreeNode::new(im_str!("Bullets")).build(ui, || { - ui.bullet_text(im_str!("Bullet point 1")); - ui.bullet_text(im_str!("Bullet point 2\nOn multiple lines")); + TreeNode::new("Bullets").build(ui, || { + ui.bullet_text("Bullet point 1"); + ui.bullet_text("Bullet point 2\nOn multiple lines"); ui.bullet(); - ui.text(im_str!("Bullet point 3 (two calls)")); + ui.text("Bullet point 3 (two calls)"); ui.bullet(); - ui.small_button(im_str!("Button")); + ui.small_button("Button"); }); - TreeNode::new(im_str!("Colored text")).build(ui, || { - ui.text_colored([1.0, 0.0, 1.0, 1.0], im_str!("Pink")); - ui.text_colored([1.0, 1.0, 0.0, 1.0], im_str!("Yellow")); - ui.text_disabled(im_str!("Disabled")); + TreeNode::new("Colored text").build(ui, || { + ui.text_colored([1.0, 0.0, 1.0, 1.0], "Pink"); + ui.text_colored([1.0, 1.0, 0.0, 1.0], "Yellow"); + ui.text_disabled("Disabled"); }); - TreeNode::new(im_str!("Multi-line text")).build(ui, || { + TreeNode::new("Multi-line text").build(ui, || { ui.input_text_multiline( - im_str!("multiline"), + "multiline", &mut state.text_multiline, [300., 100.], ).build(); }); - TreeNode::new(im_str!("Word wrapping")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("Word wrapping").build(ui, || { + ui.text_wrapped( "This text should automatically wrap on the edge of \ the window.The current implementation for text \ wrapping follows simple rulessuitable for English \ and possibly other languages." - )); + ); ui.spacing(); - Slider::new(im_str!("Wrap width"), -20.0, 600.0) + Slider::new("Wrap width", -20.0, 600.0) .display_format("%.0f") .build(ui, &mut state.wrap_width); - ui.text(im_str!("Test paragraph 1:")); + ui.text("Test paragraph 1:"); // TODO - ui.text(im_str!("Test paragraph 2:")); + ui.text("Test paragraph 2:"); // TODO }); - TreeNode::new(im_str!("UTF-8 Text")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("UTF-8 Text").build(ui, || { + ui.text_wrapped( "CJK text will only appear if the font was loaded \ with theappropriate CJK character ranges. Call \ io.Font->LoadFromFileTTF()manually to load extra \ character ranges." - )); + ); - ui.text(im_str!("Hiragana: かきくけこ (kakikukeko)")); - ui.text(im_str!("Kanjis: 日本語 (nihongo)")); - ui.input_text(im_str!("UTF-8 input"), &mut state.buf) + ui.text("Hiragana: かきくけこ (kakikukeko)"); + ui.text("Kanjis: 日本語 (nihongo)"); + ui.input_text("UTF-8 input", &mut state.buf) .build(); }); - ui.radio_button(im_str!("radio a"), &mut state.radio_button, 0); + ui.radio_button("radio a", &mut state.radio_button, 0); ui.same_line(); - ui.radio_button(im_str!("radio b"), &mut state.radio_button, 1); + ui.radio_button("radio b", &mut state.radio_button, 1); ui.same_line(); - ui.radio_button(im_str!("radio c"), &mut state.radio_button, 2); + ui.radio_button("radio c", &mut state.radio_button, 2); ui.separator(); - ui.label_text(im_str!("label"), im_str!("Value")); + ui.label_text("label", "Value"); ui.combo_simple_string("combo", &mut state.item, &[ - im_str!("aaaa"), - im_str!("bbbb"), - im_str!("cccc"), - im_str!("dddd"), - im_str!("eeee"), + "aaaa", + "bbbb", + "cccc", + "dddd", + "eeee", ]); let items = [ - im_str!("AAAA"), - im_str!("BBBB"), - im_str!("CCCC"), - im_str!("DDDD"), - im_str!("EEEE"), - im_str!("FFFF"), - im_str!("GGGG"), - im_str!("HHHH"), - im_str!("IIII"), - im_str!("JJJJ"), - im_str!("KKKK"), + "AAAA", + "BBBB", + "CCCC", + "DDDD", + "EEEE", + "FFFF", + "GGGG", + "HHHH", + "IIII", + "JJJJ", + "KKKK", ]; ui.combo_simple_string("combo scroll", &mut state.item2, &items); - ui.list_box(im_str!("list"), &mut state.item3, &items, 8); + ui.list_box("list", &mut state.item3, &items, 8); let names = [ - im_str!("Bream"), - im_str!("Haddock"), - im_str!("Mackerel"), - im_str!("Pollock"), - im_str!("Tilefish"), + "Bream", + "Haddock", + "Mackerel", + "Pollock", + "Tilefish", ]; ListBox::new("selectables list").build(ui, || { @@ -524,7 +520,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { }); let last_size = ui.item_rect_size(); - ListBox::new(im_str!("selectable list 2")).size([0.0, last_size[1] * 0.66]).build(ui, || { + ListBox::new("selectable list 2").size([0.0, last_size[1] * 0.66]).build(ui, || { for (index, name) in names.iter().enumerate() { let selected = matches!(state.selected_fish2, Some(i) if i == index ); if Selectable::new(name).selected(selected).build(ui) { @@ -533,40 +529,40 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { } }); - ui.input_text(im_str!("input text"), &mut state.text) + ui.input_text("input text", &mut state.text) .build(); - ui.input_text(im_str!("input text with hint"), &mut state.text_with_hint) - .hint(im_str!("enter text here")) + ui.input_text("input text with hint", &mut state.text_with_hint) + .hint("enter text here") .build(); - ui.input_int(im_str!("input int"), &mut state.i0).build(); - // Drag::new(im_str!("drag int")).build(ui, &mut state.i0); - ui.input_float(im_str!("input float"), &mut state.f0) + ui.input_int("input int", &mut state.i0).build(); + // Drag::new("drag int").build(ui, &mut state.i0); + ui.input_float("input float", &mut state.f0) .step(0.01) .step_fast(1.0) .build(); - Drag::new(im_str!("drag float")).range(-1.0, 1.0).speed(0.001).build(ui, &mut state.f0); - ui.input_float3(im_str!("input float3"), &mut state.vec3f) + Drag::new("drag float").range(-1.0, 1.0).speed(0.001).build(ui, &mut state.f0); + ui.input_float3("input float3", &mut state.vec3f) .build(); - ColorEdit::new(im_str!("color 1"), &mut state.col1).build(ui); - ColorEdit::new(im_str!("color 2"), &mut state.col2).build(ui); + ColorEdit::new("color 1", &mut state.col1).build(ui); + ColorEdit::new("color 2", &mut state.col2).build(ui); - TreeNode::new(im_str!("Multi-component Widgets")).build(ui, || { - ui.input_float2(im_str!("input float2"), &mut state.vec2f) + TreeNode::new("Multi-component Widgets").build(ui, || { + ui.input_float2("input float2", &mut state.vec2f) .build(); - ui.input_int2(im_str!("input int2"), &mut state.vec2i) + ui.input_int2("input int2", &mut state.vec2i) .build(); ui.spacing(); - ui.input_float3(im_str!("input float3"), &mut state.vec3f) + ui.input_float3("input float3", &mut state.vec3f) .build(); - ui.input_int3(im_str!("input int3"), &mut state.vec3i) + ui.input_int3("input int3", &mut state.vec3i) .build(); ui.spacing(); }); - TreeNode::new(im_str!("Color/Picker Widgets")).build(ui, || { + TreeNode::new("Color/Picker Widgets").build(ui, || { let s = &mut state.color_edit; - ui.checkbox(im_str!("With HDR"), &mut s.hdr); + ui.checkbox("With HDR", &mut s.hdr); ui.same_line(); show_help_marker( ui, @@ -574,12 +570,12 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { limits on dragging widgets.", ); - ui.checkbox(im_str!("With Alpha Preview"), &mut s.alpha_preview); + ui.checkbox("With Alpha Preview", &mut s.alpha_preview); ui.checkbox( - im_str!("With Half Alpha Preview"), + "With Half Alpha Preview", &mut s.alpha_half_preview, ); - ui.checkbox(im_str!("With Options Menu"), &mut s.options_menu); + ui.checkbox("With Options Menu", &mut s.options_menu); ui.same_line(); show_help_marker( ui, @@ -597,31 +593,31 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { f }; - ui.text(im_str!("Color widget:")); + ui.text("Color widget:"); ui.same_line(); show_help_marker( ui, "Click on the colored square to open a color picker. CTRL+click on individual component to input value.\n", ); - ColorEdit::new(im_str!("MyColor##1"), &mut s.color) + ColorEdit::new("MyColor##1", &mut s.color) .flags(misc_flags) .alpha(false) .build(ui); - ui.text(im_str!("Color widget HSV with Alpha:")); - ColorEdit::new(im_str!("MyColor##2"), &mut s.color) + ui.text("Color widget HSV with Alpha:"); + ColorEdit::new("MyColor##2", &mut s.color) .flags(misc_flags) .input_mode(ColorEditInputMode::HSV) .build(ui); - ui.text(im_str!("Color widget with Float Display:")); - ColorEdit::new(im_str!("MyColor##2f"), &mut s.color) + ui.text("Color widget with Float Display:"); + ColorEdit::new("MyColor##2f", &mut s.color) .flags(misc_flags) .format(ColorFormat::Float) .build(ui); - ui.text(im_str!("Color button with Picker:")); + ui.text("Color button with Picker:"); ui.same_line(); show_help_marker( ui, @@ -630,29 +626,29 @@ CTRL+click on individual component to input value.\n", With the label(false) function you can pass a non-empty label which \ will only be used for the tooltip and picker popup.", ); - ColorEdit::new(im_str!("MyColor##3"), &mut s.color) + ColorEdit::new("MyColor##3", &mut s.color) .flags(misc_flags) .inputs(false) .label(false) .build(ui); - ui.text(im_str!("Color picker:")); - ui.checkbox(im_str!("With Alpha"), &mut s.alpha); - ui.checkbox(im_str!("With Alpha Bar"), &mut s.alpha_bar); - ui.checkbox(im_str!("With Side Preview"), &mut s.side_preview); + ui.text("Color picker:"); + ui.checkbox("With Alpha", &mut s.alpha); + ui.checkbox("With Alpha Bar", &mut s.alpha_bar); + ui.checkbox("With Side Preview", &mut s.side_preview); if s.side_preview { ui.same_line(); - ui.checkbox(im_str!("With Ref Color"), &mut s.ref_color); + ui.checkbox("With Ref Color", &mut s.ref_color); if s.ref_color { ui.same_line(); - ColorEdit::new(im_str!("##RefColor"), &mut s.ref_color_v) + ColorEdit::new("##RefColor", &mut s.ref_color_v) .flags(misc_flags) .inputs(false) .build(ui); } } let mut b = ColorPicker::new - (im_str!("MyColor##4"), &mut s.color) + ("MyColor##4", &mut s.color) .flags(misc_flags) .alpha(s.alpha) .alpha_bar(s.alpha_bar) @@ -666,48 +662,48 @@ CTRL+click on individual component to input value.\n", }); } - if CollapsingHeader::new(im_str!("Layout")).build(ui) { - TreeNode::new(im_str!("Tabs")).build(ui, || { - TreeNode::new(im_str!("Basic")).build(ui, || { - TabBar::new(im_str!("basictabbar")).build(ui, || { - TabItem::new(im_str!("Avocado")).build(ui, || { - ui.text(im_str!("This is the Avocado tab!")); - ui.text(im_str!("blah blah blah blah blah")); + if CollapsingHeader::new("Layout").build(ui) { + TreeNode::new("Tabs").build(ui, || { + TreeNode::new("Basic").build(ui, || { + TabBar::new("basictabbar").build(ui, || { + TabItem::new("Avocado").build(ui, || { + ui.text("This is the Avocado tab!"); + ui.text("blah blah blah blah blah"); }); - TabItem::new(im_str!("Broccoli")).build(ui, || { - ui.text(im_str!("This is the Broccoli tab!")); - ui.text(im_str!("blah blah blah blah blah")); + TabItem::new("Broccoli").build(ui, || { + ui.text("This is the Broccoli tab!"); + ui.text("blah blah blah blah blah"); }); - TabItem::new(im_str!("Cucumber")).build(ui, || { - ui.text(im_str!("This is the Cucumber tab!")); - ui.text(im_str!("blah blah blah blah blah")); + TabItem::new("Cucumber").build(ui, || { + ui.text("This is the Cucumber tab!"); + ui.text("blah blah blah blah blah"); }); }); }); - TreeNode::new(im_str!("Advanced & Close button")).build(ui, || { + TreeNode::new("Advanced & Close button").build(ui, || { ui.separator(); let s = &mut state.tabs; - ui.checkbox(im_str!("ImGuiTabBarFlags_Reorderable"), &mut s.reorderable); - ui.checkbox(im_str!("ImGuiTabBarFlags_AutoSelectNewTabs"), &mut s.autoselect); - ui.checkbox(im_str!("ImGuiTabBarFlags_TabListPopupButton"), &mut s.listbutton); - ui.checkbox(im_str!("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton"), &mut s.noclose_middlebutton); - if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyResizeDown"), &mut s.fitting_resizedown) { + ui.checkbox("ImGuiTabBarFlags_Reorderable", &mut s.reorderable); + ui.checkbox("ImGuiTabBarFlags_AutoSelectNewTabs", &mut s.autoselect); + ui.checkbox("ImGuiTabBarFlags_TabListPopupButton", &mut s.listbutton); + ui.checkbox("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &mut s.noclose_middlebutton); + if ui.checkbox("ImGuiTabBarFlags_FittingPolicyResizeDown", &mut s.fitting_resizedown) { s.fitting_scroll = !s.fitting_resizedown; } - if ui.checkbox(im_str!("ImGuiTabBarFlags_FittingPolicyScroll"), &mut s.fitting_scroll) { + if ui.checkbox("ImGuiTabBarFlags_FittingPolicyScroll", &mut s.fitting_scroll) { s.fitting_resizedown = !s.fitting_scroll; } let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0])); - ui.checkbox(im_str!("Artichoke"), &mut s.artichoke_tab); + ui.checkbox("Artichoke", &mut s.artichoke_tab); ui.same_line(); - ui.checkbox(im_str!("Beetroot"), &mut s.beetroot_tab); + ui.checkbox("Beetroot", &mut s.beetroot_tab); ui.same_line(); - ui.checkbox(im_str!("Celery"), &mut s.celery_tab); + ui.checkbox("Celery", &mut s.celery_tab); ui.same_line(); - ui.checkbox(im_str!("Daikon"), &mut s.daikon_tab); + ui.checkbox("Daikon", &mut s.daikon_tab); style.pop(); let flags = { @@ -721,48 +717,48 @@ CTRL+click on individual component to input value.\n", f }; - TabBar::new(im_str!("tabbar")).flags(flags).build(ui, || { - TabItem::new(im_str!("Artichoke")).opened(&mut s.artichoke_tab).build(ui, || { - ui.text(im_str!("This is the Artichoke tab!")); + TabBar::new("tabbar").flags(flags).build(ui, || { + TabItem::new("Artichoke").opened(&mut s.artichoke_tab).build(ui, || { + ui.text("This is the Artichoke tab!"); }); - TabItem::new(im_str!("Beetroot")).opened(&mut s.beetroot_tab).build(ui, || { - ui.text(im_str!("This is the Beetroot tab!")); + TabItem::new("Beetroot").opened(&mut s.beetroot_tab).build(ui, || { + ui.text("This is the Beetroot tab!"); }); - TabItem::new(im_str!("Celery")).opened(&mut s.celery_tab).build(ui, || { - ui.text(im_str!("This is the Celery tab!")); + TabItem::new("Celery").opened(&mut s.celery_tab).build(ui, || { + ui.text("This is the Celery tab!"); }); - TabItem::new(im_str!("Daikon")).opened(&mut s.daikon_tab).build(ui, || { - ui.text(im_str!("This is the Daikon tab!")); + TabItem::new("Daikon").opened(&mut s.daikon_tab).build(ui, || { + ui.text("This is the Daikon tab!"); }); }); }); }); } - if CollapsingHeader::new(im_str!("Popups & Modal windows")).build(ui) { - TreeNode::new(im_str!("Popups")).build(ui, || { - ui.text_wrapped(im_str!( + if CollapsingHeader::new("Popups & Modal windows").build(ui) { + TreeNode::new("Popups").build(ui, || { + ui.text_wrapped( "When a popup is active, it inhibits interacting \ with windows that are behind the popup. Clicking \ outside the popup closes it." - )); + ); let names = [ - im_str!("Bream"), - im_str!("Haddock"), - im_str!("Mackerel"), - im_str!("Pollock"), - im_str!("Tilefish"), + "Bream", + "Haddock", + "Mackerel", + "Pollock", + "Tilefish", ]; - if ui.small_button(im_str!("Select..")) { - ui.open_popup(im_str!("select")); + if ui.small_button("Select..") { + ui.open_popup("select"); } ui.same_line(); ui.text(match state.selected_fish { Some(index) => names[index], - None => im_str!(""), + None => "", }); - ui.popup(im_str!("select"), || { - ui.text(im_str!("Aquarium")); + ui.popup("select", || { + ui.text("Aquarium"); ui.separator(); for (index, name) in names.iter().enumerate() { if Selectable::new(name).build(ui) { @@ -772,56 +768,56 @@ CTRL+click on individual component to input value.\n", }); }); - TreeNode::new(im_str!("Modals")).build(ui, || { - ui.text_wrapped(im_str!( + TreeNode::new("Modals").build(ui, || { + ui.text_wrapped( "Modal windows are like popups but the user cannot close \ them by clicking outside the window." - )); + ); - if ui.button(im_str!("Delete..")) { - ui.open_popup(im_str!("Delete?")); + if ui.button("Delete..") { + ui.open_popup("Delete?"); } - PopupModal::new(im_str!("Delete?")).always_auto_resize(true).build(ui, || { + PopupModal::new("Delete?").always_auto_resize(true).build(ui, || { ui.text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); ui.separator(); let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0])); - ui.checkbox(im_str!("Don't ask me next time"), &mut state.dont_ask_me_next_time); + ui.checkbox("Don't ask me next time", &mut state.dont_ask_me_next_time); - if ui.button_with_size(im_str!("OK"), [120.0, 0.0]) { + if ui.button_with_size("OK", [120.0, 0.0]) { ui.close_current_popup(); } ui.same_line(); - if ui.button_with_size(im_str!("Cancel"), [120.0, 0.0]) { + if ui.button_with_size("Cancel", [120.0, 0.0]) { ui.close_current_popup(); } style.pop(); }); - if ui.button(im_str!("Stacked modals..")) { - ui.open_popup(im_str!("Stacked 1")); + if ui.button("Stacked modals..") { + ui.open_popup("Stacked 1"); } - PopupModal::new(im_str!("Stacked 1")).build(ui, || { + PopupModal::new("Stacked 1").build(ui, || { ui.text( "Hello from Stacked The First\n\ Using style[StyleColor::ModalWindowDarkening] for darkening." ); - let items = &[im_str!("aaaa"), im_str!("bbbb"), im_str!("cccc"), im_str!("dddd"), im_str!("eeee")]; + let items = &["aaaa", "bbbb", "cccc", "dddd", "eeee"]; ui.combo_simple_string("Combo", &mut state.stacked_modals_item, items); - ColorEdit::new(im_str!("color"), &mut state.stacked_modals_color).build(ui); + ColorEdit::new("color", &mut state.stacked_modals_color).build(ui); - if ui.button(im_str!("Add another modal..")) { - ui.open_popup(im_str!("Stacked 2")) ; + if ui.button("Add another modal..") { + ui.open_popup("Stacked 2") ; } - PopupModal::new(im_str!("Stacked 2")).build(ui, || { + PopupModal::new("Stacked 2").build(ui, || { ui.text("Hello from Stacked The Second"); - if ui.button(im_str!("Close")) { + if ui.button("Close") { ui.close_current_popup(); } }); - if ui.button(im_str!("Close")) { + if ui.button("Close") { ui.close_current_popup(); } }); @@ -832,20 +828,20 @@ CTRL+click on individual component to input value.\n", fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) { if let Some(menu_bar) = ui.begin_main_menu_bar() { - if let Some(menu) = ui.begin_menu(im_str!("File")) { + if let Some(menu) = ui.begin_menu("File") { show_example_menu_file(ui, &mut state.file_menu); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Edit")) { - MenuItem::new(im_str!("Undo")).shortcut("CTRL+Z").build(ui); - MenuItem::new(im_str!("Redo")) + if let Some(menu) = ui.begin_menu("Edit") { + MenuItem::new("Undo").shortcut("CTRL+Z").build(ui); + MenuItem::new("Redo") .shortcut("CTRL+Y") .enabled(false) .build(ui); ui.separator(); - MenuItem::new(im_str!("Cut")).shortcut("CTRL+X").build(ui); - MenuItem::new(im_str!("Copy")).shortcut("CTRL+C").build(ui); - MenuItem::new(im_str!("Paste")).shortcut("CTRL+V").build(ui); + MenuItem::new("Cut").shortcut("CTRL+X").build(ui); + MenuItem::new("Copy").shortcut("CTRL+C").build(ui); + MenuItem::new("Paste").shortcut("CTRL+V").build(ui); menu.end(); } menu_bar.end(); @@ -853,19 +849,17 @@ fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) { } fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { - MenuItem::new(im_str!("(dummy menu)")) - .enabled(false) - .build(ui); - MenuItem::new(im_str!("New")).build(ui); - MenuItem::new(im_str!("Open")).shortcut("Ctrl+O").build(ui); - if let Some(menu) = ui.begin_menu(im_str!("Open Recent")) { - MenuItem::new(im_str!("fish_hat.c")).build(ui); - MenuItem::new(im_str!("fish_hat.inl")).build(ui); - MenuItem::new(im_str!("fish_hat.h")).build(ui); - if let Some(menu) = ui.begin_menu(im_str!("More..")) { - MenuItem::new(im_str!("Hello")).build(ui); - MenuItem::new(im_str!("Sailor")).build(ui); - if let Some(menu) = ui.begin_menu(im_str!("Recurse..")) { + MenuItem::new("(dummy menu)").enabled(false).build(ui); + MenuItem::new("New").build(ui); + MenuItem::new("Open").shortcut("Ctrl+O").build(ui); + if let Some(menu) = ui.begin_menu("Open Recent") { + MenuItem::new("fish_hat.c").build(ui); + MenuItem::new("fish_hat.inl").build(ui); + MenuItem::new("fish_hat.h").build(ui); + if let Some(menu) = ui.begin_menu("More..") { + MenuItem::new("Hello").build(ui); + MenuItem::new("Sailor").build(ui); + if let Some(menu) = ui.begin_menu("Recurse..") { show_example_menu_file(ui, state); menu.end(); } @@ -873,11 +867,11 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { } menu.end(); } - MenuItem::new(im_str!("Save")).shortcut("Ctrl+S").build(ui); - MenuItem::new(im_str!("Save As..")).build(ui); + MenuItem::new("Save").shortcut("Ctrl+S").build(ui); + MenuItem::new("Save As..").build(ui); ui.separator(); - if let Some(menu) = ui.begin_menu(im_str!("Options")) { - MenuItem::new(im_str!("Enabled")).build_with_ref(ui, &mut state.enabled); + if let Some(menu) = ui.begin_menu("Options") { + MenuItem::new("Enabled").build_with_ref(ui, &mut state.enabled); ChildWindow::new("child") .size([0.0, 60.0]) .border(true) @@ -886,31 +880,27 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) { ui.text(format!("Scrolling Text {}", i)); } }); - Slider::new(im_str!("Value"), 0.0, 1.0).build(ui, &mut state.f); + Slider::new("Value", 0.0, 1.0).build(ui, &mut state.f); - ui.input_float(im_str!("Input"), &mut state.f) - .step(0.1) - .build(); + ui.input_float("Input", &mut state.f).step(0.1).build(); let items = ["Yes", "No", "Maybe"]; ui.combo_simple_string("Combo", &mut state.n, &items); - ui.checkbox(im_str!("Check"), &mut state.b); + ui.checkbox("Check", &mut state.b); menu.end(); } - if let Some(menu) = ui.begin_menu(im_str!("Colors")) { + if let Some(menu) = ui.begin_menu("Colors") { for &col in StyleColor::VARIANTS.iter() { - MenuItem::new(&im_str!("{:?}", col)).build(ui); + MenuItem::new(format!("{:?}", col)).build(ui); } menu.end(); } - assert!(ui - .begin_menu_with_enabled(im_str!("Disabled"), false) - .is_none()); - MenuItem::new(im_str!("Checked")).selected(true).build(ui); - MenuItem::new(im_str!("Quit")).shortcut("Alt+F4").build(ui); + assert!(ui.begin_menu_with_enabled("Disabled", false).is_none()); + MenuItem::new("Checked").selected(true).build(ui); + MenuItem::new("Quit").shortcut("Alt+F4").build(ui); } fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &mut bool) { - Window::new(im_str!("Example: Auto-resizing window")) + Window::new("Example: Auto-resizing window") .opened(opened) .always_auto_resize(true) .build(ui, || { @@ -919,7 +909,7 @@ fn show_example_app_auto_resize(ui: &Ui, state: &mut AutoResizeState, opened: &m Note that you probably don't want to query the window size to output your content because that would create a feedback loop.", ); - Slider::new(im_str!("Number of lines"), 1, 20).build(ui, &mut state.lines); + Slider::new("Number of lines", 1, 20).build(ui, &mut state.lines); for i in 0..state.lines { ui.text(format!("{:2$}This is line {}", "", i, i as usize * 4)); } @@ -930,7 +920,7 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) { const DISTANCE: f32 = 10.0; let window_pos = [DISTANCE, DISTANCE]; let style = ui.push_style_color(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3]); - Window::new(im_str!("Example: Fixed Overlay")) + Window::new("Example: Fixed Overlay") .opened(opened) .position(window_pos, Condition::Always) .title_bar(false) @@ -953,7 +943,7 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) { } fn show_example_app_manipulating_window_title(ui: &Ui) { - Window::new(im_str!("Same title as another window##1")) + Window::new("Same title as another window##1") .position([100.0, 100.0], Condition::FirstUseEver) .build(ui, || { ui.text( @@ -961,7 +951,7 @@ fn show_example_app_manipulating_window_title(ui: &Ui) { My title is the same as window 2, but my identifier is unique.", ); }); - Window::new(im_str!("Same title as another window##2")) + Window::new("Same title as another window##2") .position([100.0, 200.0], Condition::FirstUseEver) .build(ui, || { ui.text( @@ -972,20 +962,20 @@ My title is the same as window 1, but my identifier is unique.", let chars = ['|', '/', '-', '\\']; let ch_idx = (ui.time() / 0.25) as usize & 3; let num = ui.frame_count(); // The C++ version uses rand() here - let title = im_str!("Animated title {} {}###AnimatedTitle", chars[ch_idx], num); - Window::new(&title) + let title = format!("Animated title {} {}###AnimatedTitle", chars[ch_idx], num); + Window::new(title) .position([100.0, 300.0], Condition::FirstUseEver) .build(ui, || ui.text("This window has a changing title")); } fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, opened: &mut bool) { - Window::new(im_str!("Example: Custom rendering")) + Window::new("Example: Custom rendering") .size([350.0, 560.0], Condition::FirstUseEver) .opened(opened) .build(ui, || { ui.text("Primitives"); // TODO: Add DragFloat to change value of sz - ColorEdit::new(im_str!("Color"), &mut state.col).build(ui); + ColorEdit::new("Color", &mut state.col).build(ui); let draw_list = ui.get_window_draw_list(); let p = ui.cursor_screen_pos(); let spacing = 8.0; @@ -1114,20 +1104,18 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, ui.dummy([(state.sz + spacing) * 8.0, (state.sz + spacing) * 3.0]); ui.separator(); - ui.text(im_str!("Canvas example")); - if ui.button(im_str!("Clear")) { + ui.text("Canvas example"); + if ui.button("Clear") { state.points.clear(); } if state.points.len() >= 2 { ui.same_line(); - if ui.button(im_str!("Undo")) { + if ui.button("Undo") { state.points.pop(); state.points.pop(); } } - ui.text(im_str!( - "Left-click and drag to add lines,\nRight-click to undo" - )); + ui.text("Left-click and drag to add lines,\nRight-click to undo"); // Here we are using InvisibleButton() as a convenience to // 1) advance the cursor, and // 2) allows us to use IsItemHovered() @@ -1175,7 +1163,7 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, .build(); let mut adding_preview = false; - ui.invisible_button(im_str!("canvas"), canvas_size); + ui.invisible_button("canvas", canvas_size); let mouse_pos = ui.io().mouse_pos; let mouse_pos_in_canvas = [mouse_pos[0] - canvas_pos[0], mouse_pos[1] - canvas_pos[1]]; if state.adding_line { @@ -1229,10 +1217,10 @@ fn show_example_app_custom_rendering(ui: &Ui, state: &mut CustomRenderingState, } fn show_app_log(ui: &Ui, app_log: &mut Vec) { - Window::new(im_str!("Example: Log")) + Window::new("Example: Log") .size([500.0, 400.0], Condition::FirstUseEver) .build(ui, || { - if ui.small_button(im_str!("[Debug] Add 5 entries")) { + if ui.small_button("[Debug] Add 5 entries") { let categories = ["info", "warn", "error"]; let words = [ "Bumfuzzled", @@ -1255,11 +1243,11 @@ fn show_app_log(ui: &Ui, app_log: &mut Vec) { app_log.push(text); } } - if ui.button(im_str!("Clear")) { + if ui.button("Clear") { app_log.clear(); } ui.same_line(); - if ui.button(im_str!("Copy")) { + if ui.button("Copy") { ui.set_clipboard_text(&ImString::from(app_log.join("\n"))); } ui.separator(); diff --git a/imgui-examples/examples/text_callbacks.rs b/imgui-examples/examples/text_callbacks.rs index 7c19523..4adcb4a 100644 --- a/imgui-examples/examples/text_callbacks.rs +++ b/imgui-examples/examples/text_callbacks.rs @@ -51,7 +51,7 @@ fn main() { println!("History was fired by pressing {:?}", dir); } - fn on_always(&mut self, txt: TextCallbackData<'_>) { + fn on_always(&mut self, _: TextCallbackData<'_>) { // We don't actually print this out because it will flood your log a lot! // println!("The always callback fired! It always fires."); } diff --git a/imgui-gfx-examples/examples/gfx_custom_textures.rs b/imgui-gfx-examples/examples/gfx_custom_textures.rs index 8446b8e..4c92217 100644 --- a/imgui-gfx-examples/examples/gfx_custom_textures.rs +++ b/imgui-gfx-examples/examples/gfx_custom_textures.rs @@ -62,10 +62,10 @@ impl CustomTexturesApp { } fn show_textures(&self, ui: &Ui) { - Window::new(im_str!("Hello textures")) + Window::new("Hello textures") .size([400.0, 600.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); if let Some(my_texture_id) = self.my_texture_id { ui.text("Some generated texture"); Image::new(my_texture_id, [100.0, 100.0]).build(ui); diff --git a/imgui-gfx-examples/examples/gfx_hello_world.rs b/imgui-gfx-examples/examples/gfx_hello_world.rs index 42a6b75..6c96b76 100644 --- a/imgui-gfx-examples/examples/gfx_hello_world.rs +++ b/imgui-gfx-examples/examples/gfx_hello_world.rs @@ -6,18 +6,18 @@ fn main() { let system = support::init(file!()); let window_title = if cfg!(all(feature = "directx", windows)) { - im_str!("Hello world (OpenGL)") + "Hello world (OpenGL)" } else { - im_str!("Hello world (DirectX)") + "Hello world (DirectX)" }; system.main_loop(|_, ui| { Window::new(window_title) .size([300.0, 100.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("Hello world!"); + ui.text("こんにちは世界!"); + ui.text("This...is...imgui-rs!"); ui.separator(); let mouse_pos = ui.io().mouse_pos; ui.text(format!( diff --git a/imgui-glow-renderer/examples/04_custom_textures.rs b/imgui-glow-renderer/examples/04_custom_textures.rs index 2741203..efca79c 100644 --- a/imgui-glow-renderer/examples/04_custom_textures.rs +++ b/imgui-glow-renderer/examples/04_custom_textures.rs @@ -9,7 +9,7 @@ use std::{io::Cursor, time::Instant}; use glow::HasContext; use image::{jpeg::JpegDecoder, ImageDecoder}; -use imgui::{im_str, Condition}; +use imgui::Condition; use imgui_glow_renderer::Renderer; @@ -144,10 +144,10 @@ impl TexturesUi { } fn show(&self, ui: &imgui::Ui) { - imgui::Window::new(im_str!("Hello textures")) + imgui::Window::new("Hello textures") .size([400.0, 400.0], Condition::FirstUseEver) .build(ui, || { - ui.text(im_str!("Hello textures!")); + ui.text("Hello textures!"); ui.text("Some generated texture"); imgui::Image::new(self.generated_texture, [100.0, 100.0]).build(ui); @@ -157,7 +157,7 @@ impl TexturesUi { // Example of using custom textures on a button ui.text("The Lenna buttons"); { - ui.invisible_button(im_str!("Boring Button"), [100.0, 100.0]); + ui.invisible_button("Boring Button", [100.0, 100.0]); // See also `imgui::Ui::style_color` let tint_none = [1.0, 1.0, 1.0, 1.0]; let tint_green = [0.5, 1.0, 0.5, 1.0]; @@ -187,7 +187,7 @@ impl TexturesUi { ui.same_line(); // Button using quad positioned image - ui.invisible_button(im_str!("Exciting Button"), [100.0, 100.0]); + ui.invisible_button("Exciting Button", [100.0, 100.0]); // Button bounds let min = ui.item_rect_min(); @@ -214,7 +214,7 @@ impl TexturesUi { // Rounded image { ui.same_line(); - ui.invisible_button(im_str!("Smooth Button"), [100.0, 100.0]); + ui.invisible_button("Smooth Button", [100.0, 100.0]); let draw_list = ui.get_window_draw_list(); draw_list diff --git a/imgui/src/widget/menu.rs b/imgui/src/widget/menu.rs index 76a428a..81a417c 100644 --- a/imgui/src/widget/menu.rs +++ b/imgui/src/widget/menu.rs @@ -112,16 +112,16 @@ impl<'ui> Ui<'ui> { /// Builder for a menu item. #[derive(Copy, Clone, Debug)] #[must_use] -pub struct MenuItem<'a, T> { - label: T, - shortcut: Option<&'a str>, +pub struct MenuItem { + label: Label, + shortcut: Option, selected: bool, enabled: bool, } -impl<'a, T: 'a + AsRef> MenuItem<'a, T> { +impl> MenuItem

, +pub struct ComboBox { + label: Label, + preview_value: Option, flags: ComboBoxFlags, } -impl<'a, T: AsRef, P: AsRef> ComboBox { +impl> ComboBox

) -> Self { + pub fn new(label: Label) -> Self { ComboBox { label, - preview_value, + preview_value: None, flags: ComboBoxFlags::empty(), } } +} + +impl, Preview: AsRef> ComboBox { + pub fn preview_value>( + self, + preview_value: Preview2, + ) -> ComboBox { + ComboBox { + label: self.label, + preview_value: Some(preview_value), + flags: self.flags, + } + } /// Replaces all current settings with the given flags. - #[inline] pub fn flags(mut self, flags: ComboBoxFlags) -> Self { self.flags = flags; self @@ -81,7 +93,6 @@ impl<'a, T: AsRef, P: AsRef> ComboBox { /// Enables/disables aligning the combo box popup toward the left. /// /// Disabled by default. - #[inline] pub fn popup_align_left(mut self, popup_align_left: bool) -> Self { self.flags .set(ComboBoxFlags::POPUP_ALIGN_LEFT, popup_align_left);