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, ) }