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