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
This commit is contained in:
Jack Mac 2021-09-12 02:01:07 -04:00
parent 694cd96d49
commit 3eaee3359d
10 changed files with 208 additions and 97 deletions

View File

@ -1,7 +1,7 @@
use clipboard::{ClipboardContext, ClipboardProvider}; use clipboard::{ClipboardContext, ClipboardProvider};
use imgui::ClipboardBackend; use imgui::ClipboardBackend;
pub struct ClipboardSupport(ClipboardContext); pub struct ClipboardSupport(pub ClipboardContext);
pub fn init() -> Option<ClipboardSupport> { pub fn init() -> Option<ClipboardSupport> {
ClipboardContext::new().ok().map(ClipboardSupport) ClipboardContext::new().ok().map(ClipboardSupport)

View File

@ -7,7 +7,7 @@ fn main() {
let mut buffers = vec![String::default(), String::default(), String::default()]; let mut buffers = vec![String::default(), String::default(), String::default()];
system.main_loop(move |_, ui| { system.main_loop(move |_, ui| {
Window::new(im_str!("Input text callbacks")) Window::new("Input text callbacks")
.size([500.0, 300.0], Condition::FirstUseEver) .size([500.0, 300.0], Condition::FirstUseEver)
.build(ui, || { .build(ui, || {
ui.text("You can make a variety of buffer callbacks on an Input Text"); ui.text("You can make a variety of buffer callbacks on an Input Text");
@ -24,9 +24,10 @@ fn main() {
ui.separator(); ui.separator();
ui.text("No callbacks:"); 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("buf0", &mut buffers[0]).build();
ui.input_text(im_str!("buf0"), &mut buffers[2]).build(); ui.input_text("buf1", &mut buffers[1]).build();
ui.input_text("buf2", &mut buffers[2]).build();
ui.separator(); ui.separator();
@ -50,18 +51,15 @@ fn main() {
println!("History was fired by pressing {:?}", dir); 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! // We don't actually print this out because it will flood your log a lot!
// println!("The always callback fired! It always fires."); // println!("The always callback fired! It always fires.");
} }
} }
ui.input_text( ui.input_text("All Callbacks logging", buffers.get_mut(0).unwrap())
im_str!("All Callbacks logging"), .callback(InputTextCallback::all(), AllCallback)
buffers.get_mut(0).unwrap(), .build();
)
.callback(InputTextCallback::all(), AllCallback)
.build();
ui.separator(); ui.separator();
@ -79,7 +77,7 @@ fn main() {
let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap(); let (buf0, brwchk_dance) = buffers.split_first_mut().unwrap();
let buf1 = Wrapper(&mut brwchk_dance[0]); 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) .callback(InputTextCallback::ALWAYS, buf1)
.build(); .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)) .callback(InputTextCallback::HISTORY, Wrapper2(buf0, buf1))
.build(); .build();

View File

@ -513,9 +513,10 @@ impl Renderer {
} }
fn configure_imgui_context(&self, imgui_context: &mut imgui::Context) { fn configure_imgui_context(&self, imgui_context: &mut imgui::Context) {
imgui_context.set_renderer_name(Some( imgui_context.set_renderer_name(Some(format!(
format!("imgui-rs-glow-render {}", env!("CARGO_PKG_VERSION")).into(), "imgui-rs-glow-render {}",
)); env!("CARGO_PKG_VERSION")
)));
#[cfg(feature = "vertex_offset_support")] #[cfg(feature = "vertex_offset_support")]
if self.gl_version.vertex_offset_support() { if self.gl_version.vertex_offset_support() {

View File

@ -32,6 +32,24 @@ impl ClipboardContext {
last_value: CString::default(), 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<String> {
None
}
fn set(&mut self, _: &str) {
// empty
}
} }
impl fmt::Debug for ClipboardContext { 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 { pub(crate) unsafe extern "C" fn get_clipboard_text(user_data: *mut c_void) -> *const c_char {
let result = catch_unwind(|| { let result = catch_unwind(|| {
println!("gettin!");
let ctx = &mut *(user_data as *mut ClipboardContext); let ctx = &mut *(user_data as *mut ClipboardContext);
println!("gettin!");
match ctx.backend.get() { match ctx.backend.get() {
Some(text) => { Some(text) => {
println!("gettin!");
ctx.last_value = CString::new(text).unwrap(); ctx.last_value = CString::new(text).unwrap();
ctx.last_value.as_ptr() ctx.last_value.as_ptr()
} }

View File

@ -1,5 +1,5 @@
use parking_lot::ReentrantMutex; use parking_lot::ReentrantMutex;
use std::cell::RefCell; use std::cell::{RefCell, UnsafeCell};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::ops::Drop; use std::ops::Drop;
use std::path::PathBuf; use std::path::PathBuf;
@ -55,7 +55,11 @@ pub struct Context {
log_filename: Option<CString>, log_filename: Option<CString>,
platform_name: Option<CString>, platform_name: Option<CString>,
renderer_name: Option<CString>, renderer_name: Option<CString>,
clipboard_ctx: Option<ClipboardContext>, // 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<UnsafeCell<ClipboardContext>>,
} }
// This mutex needs to be used to guard all public functions that can affect the underlying // 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 /// Sets the clipboard backend used for clipboard operations
pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) { pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
use std::borrow::BorrowMut; let clipboard_ctx: Box<UnsafeCell<_>> = Box::new(ClipboardContext::new(backend).into());
let mut clipboard_ctx = ClipboardContext::new(backend);
let io = self.io_mut(); let io = self.io_mut();
io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text); io.set_clipboard_text_fn = Some(crate::clipboard::set_clipboard_text);
io.get_clipboard_text_fn = Some(crate::clipboard::get_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<Rc<RefCell<SharedFontAtlas>>>) -> Self { fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
let _guard = CTX_MUTEX.lock(); let _guard = CTX_MUTEX.lock();
@ -236,7 +240,7 @@ impl Context {
log_filename: None, log_filename: None,
platform_name: None, platform_name: None,
renderer_name: None, renderer_name: None,
clipboard_ctx: None, clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
} }
} }
fn is_current_context(&self) -> bool { fn is_current_context(&self) -> bool {
@ -317,7 +321,7 @@ impl SuspendedContext {
log_filename: None, log_filename: None,
platform_name: None, platform_name: None,
renderer_name: None, renderer_name: None,
clipboard_ctx: None, clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
}; };
if ctx.is_current_context() { if ctx.is_current_context() {
// Oops, the context was activated -> deactivate // Oops, the context was activated -> deactivate

View File

@ -208,8 +208,14 @@ where
/// ///
/// If, for some reason, you don't want this, you can run this function to prevent this. /// 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. /// 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] #[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.flags.remove(InputTextFlags::CALLBACK_RESIZE);
self self
} }
@ -246,6 +252,8 @@ where
} }
pub fn build(self) -> bool { 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 (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity());
let mut data = UserData { let mut data = UserData {
@ -254,7 +262,7 @@ where
}; };
let data = &mut data as *mut _ as *mut c_void; let data = &mut data as *mut _ as *mut c_void;
unsafe { let o = unsafe {
if let Some(hint) = self.hint { if let Some(hint) = self.hint {
let (label, hint) = self.ui.scratch_txt_two(self.label, hint); let (label, hint) = self.ui.scratch_txt_two(self.label, hint);
sys::igInputTextWithHint( sys::igInputTextWithHint(
@ -278,7 +286,14 @@ where
data, 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<str>> InputTextMultiline<'ui
} }
pub fn build(self) -> bool { 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 (ptr, capacity) = (self.buf.as_mut_ptr(), self.buf.capacity());
let mut data = UserData { let mut data = UserData {
@ -357,7 +374,7 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef<str>> InputTextMultiline<'ui
}; };
let data = &mut data as *mut _ as *mut c_void; let data = &mut data as *mut _ as *mut c_void;
unsafe { let o = unsafe {
sys::igInputTextMultiline( sys::igInputTextMultiline(
self.ui.scratch_txt(self.label), self.ui.scratch_txt(self.label),
ptr as *mut sys::cty::c_char, ptr as *mut sys::cty::c_char,
@ -367,7 +384,14 @@ impl<'ui, 'p, T: InputTextCallbackHandler, L: AsRef<str>> InputTextMultiline<'ui
Some(callback::<T>), Some(callback::<T>),
data, 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<T: InputTextCallbackHandler>(
} }
InputTextFlags::CALLBACK_RESIZE => { InputTextFlags::CALLBACK_RESIZE => {
unsafe { unsafe {
let requested_size = (*data).BufSize as usize; let requested_size = (*data).BufTextLen as usize;
let buffer = &mut callback_data.user_data.container;
todo!() // just confirm that we ARE working with our string.
// if requested_size > buffer.capacity_with_nul() { debug_assert_eq!(
// // Refresh the buffer's length to take into account changes made by dear imgui. callback_data.user_data.container.as_ptr() as *const _,
// buffer.refresh_len(); (*data).Buf
// buffer.reserve(requested_size - buffer.0.len()); );
// debug_assert!(buffer.capacity_with_nul() >= requested_size);
// (*data).Buf = buffer.as_mut_ptr(); if requested_size > callback_data.user_data.container.capacity() {
// (*data).BufDirty = true; // 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 => { InputTextFlags::CALLBACK_CHAR_FILTER => {

View File

@ -438,18 +438,27 @@ impl<'ui> Ui<'ui> {
// Widgets: ListBox // Widgets: ListBox
impl<'ui> Ui<'ui> { impl<'ui> Ui<'ui> {
#[doc(alias = "ListBox")] #[doc(alias = "ListBox")]
pub fn list_box<'p, StringType: AsRef<ImStr> + ?Sized>( pub fn list_box<'p, StringType: AsRef<str> + ?Sized>(
&self, &self,
label: &'p ImStr, label: impl AsRef<str>,
current_item: &mut i32, current_item: &mut i32,
items: &'p [&'p StringType], items: &'p [&'p StringType],
height_in_items: i32, height_in_items: i32,
) -> bool { ) -> bool {
let items_inner: Vec<*const c_char> = let (label_ptr, items_inner) = unsafe {
items.iter().map(|item| item.as_ref().as_ptr()).collect(); 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 { unsafe {
sys::igListBoxStr_arr( sys::igListBoxStr_arr(
label.as_ptr(), label_ptr,
current_item, current_item,
items_inner.as_ptr() as *mut *const c_char, items_inner.as_ptr() as *mut *const c_char,
items_inner.len() as i32, 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<str> + ?Sized, const N: usize>(
// &self,
// label: impl AsRef<str>,
// 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> { impl<'ui> Ui<'ui> {
#[doc(alias = "PlotLines")] #[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<str>>(
&'ui self,
label: Label,
values: &'p [f32],
) -> PlotLines<'ui, 'p, Label> {
PlotLines::new(self, label, values) PlotLines::new(self, label, values)
} }
} }

View File

@ -1,23 +1,22 @@
use std::marker::PhantomData;
use std::os::raw::c_float; use std::os::raw::c_float;
use std::{f32, mem, ptr}; use std::{f32, mem};
use super::{ImStr, Ui}; use super::Ui;
#[must_use] #[must_use]
pub struct PlotLines<'ui, 'p> { pub struct PlotLines<'ui, 'p, Label, Overlay = &'static str> {
label: &'p ImStr, label: Label,
values: &'p [f32], values: &'p [f32],
values_offset: usize, values_offset: usize,
overlay_text: Option<&'p ImStr>, overlay_text: Option<Overlay>,
scale_min: f32, scale_min: f32,
scale_max: f32, scale_max: f32,
graph_size: [f32; 2], graph_size: [f32; 2],
_phantom: PhantomData<&'ui Ui<'ui>>, ui: &'ui Ui<'ui>,
} }
impl<'ui, 'p> PlotLines<'ui, 'p> { impl<'ui, 'p, Label: AsRef<str>> PlotLines<'ui, 'p, Label> {
pub const fn new(_: &Ui<'ui>, label: &'p ImStr, values: &'p [f32]) -> Self { pub fn new(ui: &'ui Ui<'ui>, label: Label, values: &'p [f32]) -> Self {
PlotLines { PlotLines {
label, label,
values, values,
@ -26,48 +25,58 @@ impl<'ui, 'p> PlotLines<'ui, 'p> {
scale_min: f32::MAX, scale_min: f32::MAX,
scale_max: f32::MAX, scale_max: f32::MAX,
graph_size: [0.0, 0.0], graph_size: [0.0, 0.0],
_phantom: PhantomData, ui,
} }
} }
}
#[inline] impl<'ui, 'p, Label: AsRef<str>, Overlay: AsRef<str>> PlotLines<'ui, 'p, Label, Overlay> {
pub const fn values_offset(mut self, values_offset: usize) -> Self { pub fn values_offset(mut self, values_offset: usize) -> Self {
self.values_offset = values_offset; self.values_offset = values_offset;
self self
} }
#[inline] pub fn overlay_text<Overlay2: AsRef<str>>(
pub const fn overlay_text(mut self, overlay_text: &'p ImStr) -> Self { self,
self.overlay_text = Some(overlay_text); overlay_text: Overlay2,
self ) -> 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 fn scale_min(mut self, scale_min: f32) -> Self {
pub const fn scale_min(mut self, scale_min: f32) -> Self {
self.scale_min = scale_min; self.scale_min = scale_min;
self self
} }
#[inline] pub fn scale_max(mut self, scale_max: f32) -> Self {
pub const fn scale_max(mut self, scale_max: f32) -> Self {
self.scale_max = scale_max; self.scale_max = scale_max;
self self
} }
#[inline] pub fn graph_size(mut self, graph_size: [f32; 2]) -> Self {
pub const fn graph_size(mut self, graph_size: [f32; 2]) -> Self {
self.graph_size = graph_size; self.graph_size = graph_size;
self self
} }
pub fn build(self) { pub fn build(self) {
unsafe { unsafe {
let (label, overlay) = self.ui.scratch_txt_with_opt(self.label, self.overlay_text);
sys::igPlotLinesFloatPtr( sys::igPlotLinesFloatPtr(
self.label.as_ptr(), label,
self.values.as_ptr() as *const c_float, self.values.as_ptr() as *const c_float,
self.values.len() as i32, self.values.len() as i32,
self.values_offset 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_min,
self.scale_max, self.scale_max,
self.graph_size.into(), self.graph_size.into(),

View File

@ -2,16 +2,7 @@ use std::ptr;
use crate::sys; use crate::sys;
use crate::window::WindowFlags; use crate::window::WindowFlags;
use crate::{ImStr, Ui}; use crate::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() }
);
/// Create a modal pop-up. /// Create a modal pop-up.
/// ///
@ -31,14 +22,14 @@ create_token!(
/// }; /// };
/// ``` /// ```
#[must_use] #[must_use]
pub struct PopupModal<'p> { pub struct PopupModal<'p, Label> {
label: &'p ImStr, label: Label,
opened: Option<&'p mut bool>, opened: Option<&'p mut bool>,
flags: WindowFlags, flags: WindowFlags,
} }
impl<'p> PopupModal<'p> { impl<'p, Label: AsRef<str>> PopupModal<'p, Label> {
pub fn new(label: &'p ImStr) -> Self { pub fn new(label: Label) -> Self {
PopupModal { PopupModal {
label, label,
opened: None, opened: None,
@ -140,7 +131,7 @@ impl<'p> PopupModal<'p> {
pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option<PopupToken<'ui>> { pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option<PopupToken<'ui>> {
let render = unsafe { let render = unsafe {
sys::igBeginPopupModal( sys::igBeginPopupModal(
self.label.as_ptr(), ui.scratch_txt(self.label),
self.opened self.opened
.map(|x| x as *mut bool) .map(|x| x as *mut bool)
.unwrap_or(ptr::null_mut()), .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 /// 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`]. /// able to close a popup without selected an option, use [`PopupModal`].
#[doc(alias = "OpenPopup")] #[doc(alias = "OpenPopup")]
pub fn open_popup(&self, str_id: &ImStr) { pub fn open_popup(&self, str_id: impl AsRef<str>) {
unsafe { sys::igOpenPopup(str_id.as_ptr(), 0) }; unsafe { sys::igOpenPopup(self.scratch_txt(str_id), 0) };
} }
/// Construct a popup that can have any kind of content. /// 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* /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
/// when you want to actual create the popup. /// when you want to actual create the popup.
#[doc(alias = "BeginPopup")] #[doc(alias = "BeginPopup")]
pub fn begin_popup(&self, str_id: &ImStr) -> Option<PopupToken<'_>> { pub fn begin_popup(&self, str_id: impl AsRef<str>) -> Option<PopupToken<'_>> {
let render = let render = unsafe {
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) }; sys::igBeginPopup(self.scratch_txt(str_id), WindowFlags::empty().bits() as i32)
};
if render { if render {
Some(PopupToken::new(self)) 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* /// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
/// when you want to actual create the popup. /// when you want to actual create the popup.
#[doc(alias = "BeginPopup")] #[doc(alias = "BeginPopup")]
pub fn popup<F>(&self, str_id: &ImStr, f: F) pub fn popup<F>(&self, str_id: impl AsRef<str>, f: F)
where where
F: FnOnce(), F: FnOnce(),
{ {
let render = if let Some(_t) = self.begin_popup(str_id) {
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) };
if render {
f(); f();
unsafe { sys::igEndPopup() };
} }
} }
/// Creates a PopupModal directly. /// Creates a PopupModal directly.
#[deprecated = "Please use PopupModal to create a modal popup."] pub fn popup_modal<'p, Label: AsRef<str>>(&self, str_id: Label) -> PopupModal<'p, Label> {
pub fn popup_modal<'p>(&self, str_id: &'p ImStr) -> PopupModal<'p> {
PopupModal::new(str_id) PopupModal::new(str_id)
} }
@ -215,3 +203,12 @@ impl<'ui> Ui<'ui> {
unsafe { sys::igCloseCurrentPopup() }; 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() }
);

View File

@ -74,6 +74,7 @@ impl UiBuffer {
} }
#[macro_export] #[macro_export]
#[deprecated = "all functions take AsRef<str> now -- use inline strings or `format` instead"]
macro_rules! im_str { macro_rules! im_str {
($e:literal $(,)?) => {{ ($e:literal $(,)?) => {{
const __INPUT: &str = concat!($e, "\0"); const __INPUT: &str = concat!($e, "\0");
@ -345,7 +346,6 @@ impl fmt::Write for ImString {
/// A UTF-8 encoded, implicitly nul-terminated string slice. /// A UTF-8 encoded, implicitly nul-terminated string slice.
#[derive(Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)] #[repr(transparent)]
#[deprecated]
pub struct ImStr([u8]); pub struct ImStr([u8]);
impl<'a> Default for &'a ImStr { impl<'a> Default for &'a ImStr {