Merge pull request #440 from sanbox-irl/feat_permissive_dropping

permissive dropping
This commit is contained in:
Jonathan Spira 2021-02-28 21:54:57 -08:00 committed by GitHub
commit 75fdec09d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 500 additions and 546 deletions

View File

@ -4,6 +4,11 @@
- Removed legacy `ImGuiDragDropFlags` from `legacy.rs`, which were accidentally not cleared when they were remade in `drag_drop.rs` in v0.7.0.
- Most tokens through the repository (eg. `WindowToken`, `TabBarToken`, `FontStackToken`, etc) now allow for permissive dropping -- i.e, you don't need to actually call the `.end()` method on them anymore. In exchange, these tokens have taken on a lifetime, which allows them to be safe. This could make some patterns impossible. Please file an issue if this causes a problem.
- `end()` no longer takes `Ui`. This is a breaking change, but hopefully should be trivial (and perhaps nice) for users to fix. Simply delete the argument, or add a `_` before the token's binding name and allow it to be dropped on its own.
- `PopupModal`'s `new` was reworked so that it didn't take `Ui` until `build` was called. This is a breaking change if you were invoking it directly. Simply move your `ui` call to `build` or `begin`.
## [0.7.0] - 2021-02-04
- Upgrade to [Dear ImGui v1.80](https://github.com/ocornut/imgui/releases/tag/v1.80). (Note that the new table functionality is not yet supported, however)

View File

@ -27,9 +27,9 @@ fn main() {
ui.text("Hello, I'm Roboto Regular!");
let _dokdo = ui.push_font(dokdo);
ui.text("Hello, I'm Dokdo Regular!");
_dokdo.pop(&ui);
_dokdo.pop();
ui.text("Hello, I'm Roboto Regular again!");
_roboto.pop(&ui);
_roboto.pop();
ui.text("Hello, I'm the default font again!");
});
});

View File

@ -338,7 +338,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
if let Some(menu_bar) = ui.begin_menu_bar() {
if let Some(menu) = ui.begin_menu(im_str!("Menu"), true) {
show_example_menu_file(ui, &mut state.file_menu);
menu.end(ui);
menu.end();
}
if let Some(menu) = ui.begin_menu(im_str!("Examples"), true) {
MenuItem::new(im_str!("Main menu bar"))
@ -363,7 +363,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
.build_with_ref(ui, &mut state.show_app_manipulating_window_title);
MenuItem::new(im_str!("Custom rendering"))
.build_with_ref(ui, &mut state.show_app_custom_rendering);
menu.end(ui);
menu.end();
}
if let Some(menu) = ui.begin_menu(im_str!("Help"), true) {
MenuItem::new(im_str!("Metrics"))
@ -372,9 +372,9 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
.build_with_ref(ui, &mut state.show_app_style_editor);
MenuItem::new(im_str!("About ImGui"))
.build_with_ref(ui, &mut state.show_app_about);
menu.end(ui);
menu.end();
}
menu_bar.end(ui);
menu_bar.end();
}
ui.spacing();
if CollapsingHeader::new(im_str!("Help")).build(&ui) {
@ -709,7 +709,7 @@ CTRL+click on individual component to input value.\n",
ui.checkbox(im_str!("Celery"), &mut s.celery_tab);
ui.same_line(0.0);
ui.checkbox(im_str!("Daikon"), &mut s.daikon_tab);
style.pop(ui);
style.pop();
let flags = {
let mut f = TabBarFlags::empty();
@ -782,7 +782,7 @@ CTRL+click on individual component to input value.\n",
if ui.button(im_str!("Delete.."), [0.0, 0.0]) {
ui.open_popup(im_str!("Delete?"));
}
ui.popup_modal(im_str!("Delete?")).always_auto_resize(true).build(|| {
PopupModal::new(im_str!("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]));
@ -795,13 +795,13 @@ CTRL+click on individual component to input value.\n",
if ui.button(im_str!("Cancel"), [120.0, 0.0]) {
ui.close_current_popup();
}
style.pop(ui);
style.pop();
});
if ui.button(im_str!("Stacked modals.."), [0.0, 0.0]) {
ui.open_popup(im_str!("Stacked 1"));
}
ui.popup_modal(im_str!("Stacked 1")).build(|| {
PopupModal::new(im_str!("Stacked 1")).build(ui, || {
ui.text(
"Hello from Stacked The First\n\
Using style[StyleColor::ModalWindowDarkening] for darkening."
@ -815,7 +815,7 @@ CTRL+click on individual component to input value.\n",
if ui.button(im_str!("Add another modal.."), [0.0, 0.0]) {
ui.open_popup(im_str!("Stacked 2")) ;
}
ui.popup_modal(im_str!("Stacked 2")).build(|| {
PopupModal::new(im_str!("Stacked 2")).build(ui, || {
ui.text("Hello from Stacked The Second");
if ui.button(im_str!("Close"), [0.0, 0.0]) {
ui.close_current_popup();
@ -835,7 +835,7 @@ 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"), true) {
show_example_menu_file(ui, &mut state.file_menu);
menu.end(ui);
menu.end();
}
if let Some(menu) = ui.begin_menu(im_str!("Edit"), true) {
MenuItem::new(im_str!("Undo"))
@ -855,9 +855,9 @@ fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) {
MenuItem::new(im_str!("Paste"))
.shortcut(im_str!("CTRL+V"))
.build(ui);
menu.end(ui);
menu.end();
}
menu_bar.end(ui);
menu_bar.end();
}
}
@ -878,11 +878,11 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
MenuItem::new(im_str!("Sailor")).build(ui);
if let Some(menu) = ui.begin_menu(im_str!("Recurse.."), true) {
show_example_menu_file(ui, state);
menu.end(ui);
menu.end();
}
menu.end(ui);
menu.end();
}
menu.end(ui);
menu.end();
}
MenuItem::new(im_str!("Save"))
.shortcut(im_str!("Ctrl+S"))
@ -909,13 +909,13 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
let items = [im_str!("Yes"), im_str!("No"), im_str!("Maybe")];
ComboBox::new(im_str!("Combo")).build_simple_string(ui, &mut state.n, &items);
ui.checkbox(im_str!("Check"), &mut state.b);
menu.end(ui);
menu.end();
}
if let Some(menu) = ui.begin_menu(im_str!("Colors"), true) {
for &col in StyleColor::VARIANTS.iter() {
MenuItem::new(&im_str!("{:?}", col)).build(ui);
}
menu.end(ui);
menu.end();
}
assert!(ui.begin_menu(im_str!("Disabled"), false).is_none());
MenuItem::new(im_str!("Checked")).selected(true).build(ui);
@ -966,7 +966,7 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) {
mouse_pos[0], mouse_pos[1]
));
});
style.pop(ui);
style.pop();
}
fn show_example_app_manipulating_window_title(ui: &Ui) {

View File

@ -1,31 +1,14 @@
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::sys;
use crate::Ui;
/// Tracks a layout group that must be ended by calling `.end()`
#[must_use]
pub struct GroupToken {
ctx: *const Context,
}
create_token!(
/// Tracks a layout group that can be ended with `end` or by dropping.
pub struct GroupToken<'ui>;
impl GroupToken {
/// Ends a layout group
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndGroup() };
}
}
impl Drop for GroupToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A GroupToken was leaked. Did you call .end()?");
}
}
}
/// Drops the layout group manually. You can also just allow this token
/// to drop on its own.
drop { sys::igEndGroup() }
);
/// # Cursor / Layout
impl<'ui> Ui<'ui> {
@ -84,7 +67,7 @@ impl<'ui> Ui<'ui> {
/// Returns a `GroupToken` that must be ended by calling `.end()`
pub fn begin_group(&self) -> GroupToken {
unsafe { sys::igBeginGroup() };
GroupToken { ctx: self.ctx }
GroupToken::new(self)
}
/// Creates a layout group and runs a closure to construct the contents.
///
@ -92,7 +75,7 @@ impl<'ui> Ui<'ui> {
pub fn group<R, F: FnOnce() -> R>(&self, f: F) -> R {
let group = self.begin_group();
let result = f();
group.end(self);
group.end();
result
}
/// Returns the cursor position (in window coordinates)

View File

@ -29,7 +29,7 @@ pub use self::legacy::*;
pub use self::list_clipper::ListClipper;
pub use self::plothistogram::PlotHistogram;
pub use self::plotlines::PlotLines;
pub use self::popup_modal::PopupModal;
pub use self::popups::*;
pub use self::render::draw_data::*;
pub use self::render::renderer::*;
pub use self::stacks::*;
@ -54,6 +54,9 @@ use internal::RawCast;
#[macro_use]
mod string;
#[macro_use]
mod tokens;
mod clipboard;
pub mod color;
mod columns;
@ -70,7 +73,7 @@ mod legacy;
mod list_clipper;
mod plothistogram;
mod plotlines;
mod popup_modal;
mod popups;
mod render;
mod stacks;
mod style;
@ -287,27 +290,14 @@ impl<'ui> Ui<'ui> {
}
}
/// Tracks a layout tooltip that must be ended by calling `.end()`
#[must_use]
pub struct TooltipToken {
ctx: *const Context,
}
create_token!(
/// Tracks a layout tooltip that can be ended by calling `.end()` or by dropping.
pub struct TooltipToken<'ui>;
impl TooltipToken {
/// Ends a layout tooltip
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndTooltip() };
}
}
impl Drop for TooltipToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A TooltipToken was leaked. Did you call .end()?");
}
}
}
/// Drops the layout tooltip manually. You can also just allow this token
/// to drop on its own.
drop { sys::igEndTooltip() }
);
/// # Tooltips
impl<'ui> Ui<'ui> {
@ -336,9 +326,9 @@ impl<'ui> Ui<'ui> {
/// Construct a tooltip window that can have any kind of content.
///
/// Returns a `TooltipToken` that must be ended by calling `.end()`
pub fn begin_tooltip(&self) -> TooltipToken {
pub fn begin_tooltip(&self) -> TooltipToken<'_> {
unsafe { sys::igBeginTooltip() };
TooltipToken { ctx: self.ctx }
TooltipToken::new(self)
}
/// Construct a tooltip window with simple text content.
///
@ -360,49 +350,6 @@ impl<'ui> Ui<'ui> {
}
}
// Widgets: Popups
impl<'ui> Ui<'ui> {
pub fn open_popup(&self, str_id: &ImStr) {
unsafe { sys::igOpenPopup(str_id.as_ptr(), 0) };
}
pub fn popup<F>(&self, str_id: &ImStr, f: F)
where
F: FnOnce(),
{
let render =
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) };
if render {
f();
unsafe { sys::igEndPopup() };
}
}
/// Create a modal pop-up.
///
/// # Example
/// ```rust,no_run
/// # use imgui::*;
/// # let mut imgui = Context::create();
/// # let ui = imgui.frame();
/// if ui.button(im_str!("Show modal"), [0.0, 0.0]) {
/// ui.open_popup(im_str!("modal"));
/// }
/// ui.popup_modal(im_str!("modal")).build(|| {
/// ui.text("Content of my modal");
/// if ui.button(im_str!("OK"), [0.0, 0.0]) {
/// ui.close_current_popup();
/// }
/// });
/// ```
pub fn popup_modal<'p>(&self, str_id: &'p ImStr) -> PopupModal<'ui, 'p> {
PopupModal::new(self, str_id)
}
/// Close a popup. Should be called within the closure given as argument to
/// [`Ui::popup`] or [`Ui::popup_modal`].
pub fn close_current_popup(&self) {
unsafe { sys::igCloseCurrentPopup() };
}
}
// Widgets: ListBox
impl<'ui> Ui<'ui> {
pub fn list_box<'p, StringType: AsRef<ImStr> + ?Sized>(

View File

@ -71,7 +71,11 @@ impl<'ui> Drop for ListClipperToken<'ui> {
sys::ImGuiListClipper_destroy(self.list_clipper);
};
} else if !thread::panicking() {
panic!("Forgot to call End(), or to Step() until false?");
panic!(
"Forgot to call End(), or to Step() until false? \
This is the only token in the repository which users must call `.end()` or `.step()` \
with. See https://github.com/imgui-rs/imgui-rs/issues/438"
);
}
}
}

View File

@ -1,120 +0,0 @@
use std::marker::PhantomData;
use std::ptr;
use crate::sys;
use crate::window::WindowFlags;
use crate::{ImStr, Ui};
/// Created by call to [`Ui::popup_modal`].
#[must_use]
pub struct PopupModal<'ui, 'p> {
label: &'p ImStr,
opened: Option<&'p mut bool>,
flags: WindowFlags,
_phantom: PhantomData<&'ui Ui<'ui>>,
}
impl<'ui, 'p> PopupModal<'ui, 'p> {
pub fn new(_: &Ui<'ui>, label: &'p ImStr) -> Self {
PopupModal {
label,
opened: None,
flags: WindowFlags::empty(),
_phantom: PhantomData,
}
}
/// Pass a mutable boolean which will be updated to refer to the current
/// "open" state of the modal.
pub fn opened(mut self, opened: &'p mut bool) -> Self {
self.opened = Some(opened);
self
}
pub fn flags(mut self, flags: WindowFlags) -> Self {
self.flags = flags;
self
}
pub fn title_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_TITLE_BAR, !value);
self
}
pub fn resizable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_RESIZE, !value);
self
}
pub fn movable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_MOVE, !value);
self
}
pub fn scroll_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
self
}
pub fn scrollable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
self
}
pub fn collapsible(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_COLLAPSE, !value);
self
}
pub fn always_auto_resize(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
self
}
pub fn save_settings(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SAVED_SETTINGS, !value);
self
}
pub fn inputs(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_INPUTS, !value);
self
}
pub fn menu_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::MENU_BAR, value);
self
}
pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
self
}
pub fn no_focus_on_appearing(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, value);
self
}
pub fn no_bring_to_front_on_focus(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, value);
self
}
pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
self
}
pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
self
}
pub fn always_use_window_padding(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
self
}
/// Consume and draw the PopupModal.
pub fn build<F: FnOnce()>(self, f: F) {
let render = unsafe {
sys::igBeginPopupModal(
self.label.as_ptr(),
self.opened
.map(|x| x as *mut bool)
.unwrap_or(ptr::null_mut()),
self.flags.bits() as i32,
)
};
if render {
f();
unsafe { sys::igEndPopup() };
}
}
}

212
imgui/src/popups.rs Normal file
View File

@ -0,0 +1,212 @@
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() }
);
/// Create a modal pop-up.
///
/// # Example
/// ```rust,no_run
/// # use imgui::*;
/// # let mut imgui = Context::create();
/// # let ui = imgui.frame();
/// if ui.button(im_str!("Show modal"), [0.0, 0.0]) {
/// ui.open_popup(im_str!("modal"));
/// }
/// if let Some(_token) = PopupModal::new(im_str!("modal")).begin_popup() {
/// ui.text("Content of my modal");
/// if ui.button(im_str!("OK"), [0.0, 0.0]) {
/// ui.close_current_popup();
/// }
/// });
/// ```
#[must_use]
pub struct PopupModal<'p> {
label: &'p ImStr,
opened: Option<&'p mut bool>,
flags: WindowFlags,
}
impl<'p> PopupModal<'p> {
pub fn new(label: &'p ImStr) -> Self {
PopupModal {
label,
opened: None,
flags: WindowFlags::empty(),
}
}
/// Pass a mutable boolean which will be updated to refer to the current
/// "open" state of the modal.
pub fn opened(mut self, opened: &'p mut bool) -> Self {
self.opened = Some(opened);
self
}
pub fn flags(mut self, flags: WindowFlags) -> Self {
self.flags = flags;
self
}
pub fn title_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_TITLE_BAR, !value);
self
}
pub fn resizable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_RESIZE, !value);
self
}
pub fn movable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_MOVE, !value);
self
}
pub fn scroll_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SCROLLBAR, !value);
self
}
pub fn scrollable(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SCROLL_WITH_MOUSE, !value);
self
}
pub fn collapsible(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_COLLAPSE, !value);
self
}
pub fn always_auto_resize(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::ALWAYS_AUTO_RESIZE, value);
self
}
pub fn save_settings(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_SAVED_SETTINGS, !value);
self
}
pub fn inputs(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_INPUTS, !value);
self
}
pub fn menu_bar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::MENU_BAR, value);
self
}
pub fn horizontal_scrollbar(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::HORIZONTAL_SCROLLBAR, value);
self
}
pub fn no_focus_on_appearing(mut self, value: bool) -> Self {
self.flags.set(WindowFlags::NO_FOCUS_ON_APPEARING, value);
self
}
pub fn no_bring_to_front_on_focus(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::NO_BRING_TO_FRONT_ON_FOCUS, value);
self
}
pub fn always_vertical_scrollbar(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_VERTICAL_SCROLLBAR, value);
self
}
pub fn always_horizontal_scrollbar(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_HORIZONTAL_SCROLLBAR, value);
self
}
pub fn always_use_window_padding(mut self, value: bool) -> Self {
self.flags
.set(WindowFlags::ALWAYS_USE_WINDOW_PADDING, value);
self
}
/// Consume and draw the PopupModal.
pub fn build<F: FnOnce()>(self, ui: &Ui<'_>, f: F) {
if let Some(_popup) = self.begin_popup(ui) {
f();
}
}
/// Consume and draw the PopupModal.
/// Construct a popup that can have any kind of content.
///
/// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
/// when you want to actual create the popup.
pub fn begin_popup<'ui>(self, ui: &Ui<'ui>) -> Option<PopupToken<'ui>> {
let render = unsafe {
sys::igBeginPopupModal(
self.label.as_ptr(),
self.opened
.map(|x| x as *mut bool)
.unwrap_or(ptr::null_mut()),
self.flags.bits() as i32,
)
};
if render {
Some(PopupToken::new(ui))
} else {
None
}
}
}
// Widgets: Popups
impl<'ui> Ui<'ui> {
/// Instructs ImGui to open a popup, which must be began with either [`begin_popup`](Self::begin_popup)
/// or [`popup`](Self::popup). You also use this function to begin [ModalPopups].
///
/// The confusing aspect to popups is that ImGui holds "control" over the popup fundamentally, so that ImGui
/// 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`].
pub fn open_popup(&self, str_id: &ImStr) {
unsafe { sys::igOpenPopup(str_id.as_ptr(), 0) };
}
/// Construct a popup that can have any kind of content.
///
/// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
/// when you want to actual create the popup.
pub fn begin_popup(&self, str_id: &ImStr) -> Option<PopupToken<'_>> {
let render =
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) };
if render {
Some(PopupToken::new(self))
} else {
None
}
}
/// Construct a popup that can have any kind of content.
///
/// This should be called *per frame*, whereas [`open_popup`](Self::open_popup) should be called *once*
/// when you want to actual create the popup.
pub fn popup<F>(&self, str_id: &ImStr, f: F)
where
F: FnOnce(),
{
let render =
unsafe { sys::igBeginPopup(str_id.as_ptr(), WindowFlags::empty().bits() as i32) };
if render {
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> {
PopupModal::new(str_id)
}
/// Close a popup. Should be called within the closure given as argument to
/// [`Ui::popup`] or [`Ui::popup_modal`].
pub fn close_current_popup(&self) {
unsafe { sys::igCloseCurrentPopup() };
}
}

View File

@ -34,13 +34,13 @@ impl<'ui> Ui<'ui> {
/// ui.text("I use the custom font!");
/// font.pop(&ui);
/// ```
pub fn push_font(&self, id: FontId) -> FontStackToken {
pub fn push_font(&self, id: FontId) -> FontStackToken<'_> {
let fonts = self.fonts();
let font = fonts
.get_font(id)
.expect("Font atlas did not contain the given font");
unsafe { sys::igPushFont(font.raw() as *const _ as *mut _) };
FontStackToken { ctx: self.ctx }
FontStackToken::new(self)
}
/// Changes a style color by pushing a change to the color stack.
///
@ -59,11 +59,9 @@ impl<'ui> Ui<'ui> {
/// ```
pub fn push_style_color(&self, style_color: StyleColor, color: [f32; 4]) -> ColorStackToken {
unsafe { sys::igPushStyleColorVec4(style_color as i32, color.into()) };
ColorStackToken {
count: 1,
ctx: self.ctx,
}
ColorStackToken::new(self)
}
/// Changes style colors by pushing several changes to the color stack.
///
/// Returns a `ColorStackToken` that must be popped by calling `.pop()`
@ -84,7 +82,8 @@ impl<'ui> Ui<'ui> {
/// ui.text_disabled("I'm green!");
/// colors.pop(&ui);
/// ```
pub fn push_style_colors<'a, I>(&self, style_colors: I) -> ColorStackToken
#[deprecated = "deprecated in 0.7.0. Use `push_style_color` multiple times for similar effect."]
pub fn push_style_colors<'a, I>(&self, style_colors: I) -> MultiColorStackToken
where
I: IntoIterator<Item = &'a (StyleColor, [f32; 4])>,
{
@ -93,14 +92,15 @@ impl<'ui> Ui<'ui> {
unsafe { sys::igPushStyleColorVec4(style_color as i32, color.into()) };
count += 1;
}
ColorStackToken {
MultiColorStackToken {
count,
ctx: self.ctx,
}
}
/// Changes a style variable by pushing a change to the style stack.
///
/// Returns a `StyleStackToken` that must be popped by calling `.pop()`
/// Returns a `StyleStackToken` that can be popped by calling `.end()`
/// or by allowing to drop.
///
/// # Examples
///
@ -114,10 +114,7 @@ impl<'ui> Ui<'ui> {
/// ```
pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken {
unsafe { push_style_var(style_var) };
StyleStackToken {
count: 1,
ctx: self.ctx,
}
StyleStackToken::new(self)
}
/// Changes style variables by pushing several changes to the style stack.
///
@ -137,7 +134,8 @@ impl<'ui> Ui<'ui> {
/// ui.text("...with large spacing as well");
/// styles.pop(&ui);
/// ```
pub fn push_style_vars<'a, I>(&self, style_vars: I) -> StyleStackToken
#[deprecated = "deprecated in 0.7.0. Use `push_style_var` multiple times for similar effect."]
pub fn push_style_vars<'a, I>(&self, style_vars: I) -> MultiStyleStackToken
where
I: IntoIterator<Item = &'a StyleVar>,
{
@ -146,43 +144,53 @@ impl<'ui> Ui<'ui> {
unsafe { push_style_var(style_var) };
count += 1;
}
StyleStackToken {
MultiStyleStackToken {
count,
ctx: self.ctx,
}
}
}
/// Tracks a font pushed to the font stack that must be popped by calling `.pop()`
#[must_use]
pub struct FontStackToken {
ctx: *const Context,
}
create_token!(
/// Tracks a font pushed to the font stack that can be popped by calling `.end()`
/// or by dropping.
pub struct FontStackToken<'ui>;
impl FontStackToken {
/// Pops a change from the font stack
pub fn pop(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igPopFont() };
/// Pops a change from the font stack.
drop { sys::igPopFont() }
);
impl FontStackToken<'_> {
/// Pops a change from the font stack.
pub fn pop(self) {
self.end()
}
}
impl Drop for FontStackToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A FontStackToken was leaked. Did you call .pop()?");
}
create_token!(
/// Tracks a color pushed to the color stack that can be popped by calling `.end()`
/// or by dropping.
pub struct ColorStackToken<'ui>;
/// Pops a change from the color stack.
drop { sys::igPopStyleColor(1) }
);
impl ColorStackToken<'_> {
/// Pops a change from the color stack.
pub fn pop(self) {
self.end()
}
}
/// Tracks one or more changes pushed to the color stack that must be popped by calling `.pop()`
#[must_use]
pub struct ColorStackToken {
pub struct MultiColorStackToken {
count: usize,
ctx: *const Context,
}
impl ColorStackToken {
impl MultiColorStackToken {
/// Pops changes from the color stack
pub fn pop(mut self, _: &Ui) {
self.ctx = ptr::null();
@ -190,7 +198,7 @@ impl ColorStackToken {
}
}
impl Drop for ColorStackToken {
impl Drop for MultiColorStackToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ColorStackToken was leaked. Did you call .pop()?");
@ -198,14 +206,30 @@ impl Drop for ColorStackToken {
}
}
create_token!(
/// Tracks a style pushed to the style stack that can be popped by calling `.end()`
/// or by dropping.
pub struct StyleStackToken<'ui>;
/// Pops a change from the style stack.
drop { sys::igPopStyleVar(1) }
);
impl StyleStackToken<'_> {
/// Pops a change from the style stack.
pub fn pop(self) {
self.end()
}
}
/// Tracks one or more changes pushed to the style stack that must be popped by calling `.pop()`
#[must_use]
pub struct StyleStackToken {
pub struct MultiStyleStackToken {
count: usize,
ctx: *const Context,
}
impl StyleStackToken {
impl MultiStyleStackToken {
/// Pops changes from the style stack
pub fn pop(mut self, _: &Ui) {
self.ctx = ptr::null();
@ -213,7 +237,7 @@ impl StyleStackToken {
}
}
impl Drop for StyleStackToken {
impl Drop for MultiStyleStackToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A StyleStackToken was leaked. Did you call .pop()?");
@ -323,7 +347,6 @@ pub enum ItemFlag {
ButtonRepeat(bool),
}
/// Tracks a change pushed to the item width stack
pub struct ItemWidthStackToken {
_ctx: *const Context,
}
@ -372,13 +395,29 @@ impl ItemFlagsStackToken {
}
}
create_token!(
/// Tracks an ID pushed to the ID stack that can be popped by calling `.pop()`
/// or by dropping.
pub struct IdStackToken<'ui>;
/// Pops a change from the ID stack
drop { sys::igPopID() }
);
impl IdStackToken<'_> {
/// Pops a change from the ID stack
pub fn pop(self) {
self.end()
}
}
/// # ID stack
impl<'ui> Ui<'ui> {
/// Pushes an identifier to the ID stack.
///
/// Returns an `IdStackToken` that must be popped by calling `.pop()`
///
pub fn push_id<'a, I: Into<Id<'a>>>(&self, id: I) -> IdStackToken {
/// Returns an `IdStackToken` that can be popped by calling `.end()`
/// or by dropping manually.
pub fn push_id<'a, I: Into<Id<'a>>>(&self, id: I) -> IdStackToken<'ui> {
let id = id.into();
unsafe {
@ -392,28 +431,6 @@ impl<'ui> Ui<'ui> {
Id::Ptr(p) => sys::igPushIDPtr(p as *const c_void),
}
}
IdStackToken { ctx: self.ctx }
}
}
/// Tracks an ID pushed to the ID stack that must be popped by calling `.pop()`
#[must_use]
pub struct IdStackToken {
ctx: *const Context,
}
impl IdStackToken {
/// Pops a change from the ID stack
pub fn pop(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igPopID() };
}
}
impl Drop for IdStackToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A IdStackToken was leaked. Did you call .pop()?");
}
IdStackToken::new(self)
}
}

43
imgui/src/tokens.rs Normal file
View File

@ -0,0 +1,43 @@
#[macro_export]
/// This is a macro used internally by imgui-rs to create StackTokens
/// representing various global state in DearImGui.
///
/// These tokens can either be allowed to drop or dropped manually
/// by called `end` on them. Preventing this token from dropping,
/// or moving this token out of the block it was made in can have
/// unintended side effects, including failed asserts in the DearImGui C++.
///
/// In general, if you're looking at this, don't overthink these -- just slap
/// a '_token` as their binding name and allow them to drop.
macro_rules! create_token {
(
$(#[$struct_meta:meta])*
$v:vis struct $token_name:ident<'ui>;
$(#[$end_meta:meta])*
drop { $on_drop:expr }
) => {
#[must_use]
$(#[$struct_meta])*
pub struct $token_name<'a>($crate::__core::marker::PhantomData<crate::Ui<'a>>);
impl<'a> $token_name<'a> {
/// Creates a new token type.
pub(crate) fn new(_: &crate::Ui<'a>) -> Self {
Self(std::marker::PhantomData)
}
$(#[$end_meta])*
#[inline]
pub fn end(self) {
// left empty for drop
}
}
impl Drop for $token_name<'_> {
fn drop(&mut self) {
unsafe { $on_drop }
}
}
}
}

View File

@ -1,9 +1,7 @@
use bitflags::bitflags;
use std::borrow::Cow;
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
@ -141,7 +139,7 @@ impl<'a> ComboBox<'a> {
///
/// Returns `None` if the combo box is not open and no content should be rendered.
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<ComboBoxToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
let should_render = unsafe {
sys::igBeginCombo(
self.label.as_ptr(),
@ -150,7 +148,7 @@ impl<'a> ComboBox<'a> {
)
};
if should_render {
Some(ComboBoxToken { ctx: ui.ctx })
Some(ComboBoxToken::new(ui))
} else {
None
}
@ -159,34 +157,20 @@ impl<'a> ComboBox<'a> {
///
/// Note: the closure is not called if the combo box is not open.
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(combo) = self.begin(ui) {
if let Some(_combo) = self.begin(ui) {
f();
combo.end(ui);
}
}
}
/// Tracks a combo box that must be ended by calling `.end()`
#[must_use]
pub struct ComboBoxToken {
ctx: *const Context,
}
create_token!(
/// Tracks a combo box that can be ended by calling `.end()`
/// or by dropping.
pub struct ComboBoxToken<'ui>;
impl ComboBoxToken {
/// Ends a combo box
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndCombo() };
}
}
impl Drop for ComboBoxToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ComboBoxToken was leaked. Did you call .end()?");
}
}
}
drop { sys::igEndCombo() }
);
/// # Convenience functions
impl<'a> ComboBox<'a> {
@ -219,7 +203,6 @@ impl<'a> ComboBox<'a> {
result = true;
}
}
_cb.end(ui);
}
result
}

View File

@ -1,8 +1,5 @@
use std::borrow::Cow;
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
@ -62,7 +59,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(self, ui: &Ui) -> Option<ListBoxToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ListBoxToken<'ui>> {
let should_render = unsafe {
match self.size {
Size::Vec(size) => sys::igListBoxHeaderVec2(self.label.as_ptr(), size),
@ -73,7 +70,7 @@ impl<'a> ListBox<'a> {
}
};
if should_render {
Some(ListBoxToken { ctx: ui.ctx })
Some(ListBoxToken::new(ui))
} else {
None
}
@ -82,34 +79,20 @@ impl<'a> ListBox<'a> {
///
/// Note: the closure is not called if the list box is not open.
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(list) = self.begin(ui) {
if let Some(_list) = self.begin(ui) {
f();
list.end(ui);
}
}
}
/// Tracks a list box that must be ended by calling `.end()`
#[must_use]
pub struct ListBoxToken {
ctx: *const Context,
}
create_token!(
/// Tracks a list box that can be ended by calling `.end()`
/// or by dropping
pub struct ListBoxToken<'ui>;
impl ListBoxToken {
/// Ends a list box
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igListBoxFooter() };
}
}
impl Drop for ListBoxToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ListBoxToken was leaked. Did you call .end()?");
}
}
}
drop { sys::igListBoxFooter() }
);
/// # Convenience functions
impl<'a> ListBox<'a> {
@ -136,7 +119,6 @@ impl<'a> ListBox<'a> {
result = true;
}
}
_cb.end(ui);
}
result
}

View File

@ -1,7 +1,5 @@
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
@ -15,9 +13,9 @@ impl<'ui> Ui<'ui> {
///
/// Returns `None` if the menu bar is not visible and no content should be rendered.
#[must_use]
pub fn begin_main_menu_bar(&self) -> Option<MainMenuBarToken> {
pub fn begin_main_menu_bar(&self) -> Option<MainMenuBarToken<'ui>> {
if unsafe { sys::igBeginMainMenuBar() } {
Some(MainMenuBarToken { ctx: self.ctx })
Some(MainMenuBarToken::new(self))
} else {
None
}
@ -26,9 +24,8 @@ impl<'ui> Ui<'ui> {
///
/// Note: the closure is not called if the menu bar is not visible.
pub fn main_menu_bar<F: FnOnce()>(&self, f: F) {
if let Some(menu_bar) = self.begin_main_menu_bar() {
if let Some(_menu_bar) = self.begin_main_menu_bar() {
f();
menu_bar.end(self);
}
}
/// Creates and starts appending to the menu bar of the current window.
@ -38,9 +35,9 @@ impl<'ui> Ui<'ui> {
///
/// Returns `None` if the menu bar is not visible and no content should be rendered.
#[must_use]
pub fn begin_menu_bar(&self) -> Option<MenuBarToken> {
pub fn begin_menu_bar(&self) -> Option<MenuBarToken<'_>> {
if unsafe { sys::igBeginMenuBar() } {
Some(MenuBarToken { ctx: self.ctx })
Some(MenuBarToken::new(self))
} else {
None
}
@ -49,9 +46,8 @@ impl<'ui> Ui<'ui> {
///
/// Note: the closure is not called if the menu bar is not visible.
pub fn menu_bar<F: FnOnce()>(&self, f: F) {
if let Some(menu_bar) = self.begin_menu_bar() {
if let Some(_menu_bar) = self.begin_menu_bar() {
f();
menu_bar.end(self);
}
}
/// Creates and starts appending to a sub-menu entry.
@ -61,9 +57,9 @@ impl<'ui> Ui<'ui> {
///
/// Returns `None` if the menu is not visible and no content should be rendered.
#[must_use]
pub fn begin_menu(&self, label: &ImStr, enabled: bool) -> Option<MenuToken> {
pub fn begin_menu(&self, label: &ImStr, enabled: bool) -> Option<MenuToken<'_>> {
if unsafe { sys::igBeginMenu(label.as_ptr(), enabled) } {
Some(MenuToken { ctx: self.ctx })
Some(MenuToken::new(self))
} else {
None
}
@ -72,9 +68,8 @@ impl<'ui> Ui<'ui> {
///
/// Note: the closure is not called if the menu is not visible.
pub fn menu<F: FnOnce()>(&self, label: &ImStr, enabled: bool, f: F) {
if let Some(menu) = self.begin_menu(label, enabled) {
if let Some(_menu) = self.begin_menu(label, enabled) {
f();
menu.end(self);
}
}
}
@ -151,68 +146,29 @@ impl<'a> MenuItem<'a> {
}
}
/// Tracks a main menu bar that must be ended by calling `.end()`
#[must_use]
pub struct MainMenuBarToken {
ctx: *const Context,
}
create_token!(
/// Tracks a main menu bar that can be ended by calling `.end()`
/// or by dropping
pub struct MainMenuBarToken<'ui>;
impl MainMenuBarToken {
/// Ends a main menu bar
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndMainMenuBar() };
}
}
drop { sys::igEndMainMenuBar() }
);
impl Drop for MainMenuBarToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A MainMenuBarToken was leaked. Did you call .end()?");
}
}
}
create_token!(
/// Tracks a menu bar that can be ended by calling `.end()`
/// or by dropping
pub struct MenuBarToken<'ui>;
/// Tracks a menu bar that must be ended by calling `.end()`
#[must_use]
pub struct MenuBarToken {
ctx: *const Context,
}
impl MenuBarToken {
/// Ends a menu bar
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndMenuBar() };
}
}
drop { sys::igEndMenuBar() }
);
impl Drop for MenuBarToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A MenuBarToken was leaked. Did you call .end()?");
}
}
}
create_token!(
/// Tracks a menu that can be ended by calling `.end()`
/// or by dropping
pub struct MenuToken<'ui>;
/// Tracks a menu that must be ended by calling `.end()`
#[must_use]
pub struct MenuToken {
ctx: *const Context,
}
impl MenuToken {
/// Ends a menu
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndMenu() };
}
}
impl Drop for MenuToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A MenuToken was leaked. Did you call .end()?");
}
}
}
drop { sys::igEndMenu() }
);

View File

@ -19,12 +19,11 @@
//! ```
//!
//! See `test_window_impl.rs` for a more complicated example.
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::Ui;
use bitflags::bitflags;
use std::{ptr, thread};
use std::ptr;
bitflags! {
#[repr(transparent)]
@ -90,12 +89,12 @@ impl<'a> TabBar<'a> {
}
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<TabBarToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<TabBarToken<'ui>> {
let should_render =
unsafe { sys::igBeginTabBar(self.id.as_ptr(), self.flags.bits() as i32) };
if should_render {
Some(TabBarToken { ctx: ui.ctx })
Some(TabBarToken::new(ui))
} else {
unsafe { sys::igEndTabBar() };
None
@ -106,33 +105,20 @@ impl<'a> TabBar<'a> {
///
/// Note: the closure is not called if no tabbar content is visible
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(tab) = self.begin(ui) {
if let Some(_tab) = self.begin(ui) {
f();
tab.end(ui);
}
}
}
/// Tracks a window that must be ended by calling `.end()`
pub struct TabBarToken {
ctx: *const Context,
}
create_token!(
/// Tracks a window that can be ended by calling `.end()`
/// or by dropping
pub struct TabBarToken<'ui>;
impl TabBarToken {
/// Ends a tab bar
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndTabBar() };
}
}
impl Drop for TabBarToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A TabBarToken was leaked. Did you call .end()?");
}
}
}
/// Ends a tab bar.
drop { sys::igEndTabBar() }
);
pub struct TabItem<'a> {
name: &'a ImStr,
@ -168,7 +154,7 @@ impl<'a> TabItem<'a> {
}
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<TabItemToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<TabItemToken<'ui>> {
let should_render = unsafe {
sys::igBeginTabItem(
self.name.as_ptr(),
@ -180,7 +166,7 @@ impl<'a> TabItem<'a> {
};
if should_render {
Some(TabItemToken { ctx: ui.ctx })
Some(TabItemToken::new(ui))
} else {
None
}
@ -190,28 +176,17 @@ impl<'a> TabItem<'a> {
///
/// Note: the closure is not called if the tab item is not selected
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(tab) = self.begin(ui) {
if let Some(_tab) = self.begin(ui) {
f();
tab.end(ui);
}
}
}
pub struct TabItemToken {
ctx: *const Context,
}
create_token!(
/// Tracks a tab bar item that can be ended by calling `.end()`
/// or by dropping
pub struct TabItemToken<'ui>;
impl TabItemToken {
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndTabItem() };
}
}
impl Drop for TabItemToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A TabItemToken was leaked. Did you call .end()?");
}
}
}
/// Ends a tab bar item.
drop { sys::igEndTabItem() }
);

View File

@ -26,14 +26,14 @@ impl<'ui> Ui<'ui> {
pub fn text_colored<T: AsRef<str>>(&self, color: [f32; 4], text: T) {
let style = self.push_style_color(StyleColor::Text, color);
self.text(text);
style.pop(self);
style.end();
}
/// Renders simple text using `StyleColor::TextDisabled` color
pub fn text_disabled<T: AsRef<str>>(&self, text: T) {
let color = self.style_color(StyleColor::TextDisabled);
let style = self.push_style_color(StyleColor::Text, color);
self.text(text);
style.pop(self);
style.end();
}
/// Renders text wrapped to the end of window (or column)
pub fn text_wrapped(&self, text: &ImStr) {

View File

@ -1,9 +1,6 @@
use bitflags::bitflags;
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::{Condition, Ui};
@ -234,11 +231,10 @@ impl<'a> TreeNode<'a> {
/// Pushes a tree node and starts appending to it.
///
/// Returns `Some(TreeNodeToken)` if the tree node is open. After content has been
/// rendered, the token must be popped by calling `.pop()`.
/// rendered, the token can be popped by calling `.pop()`.
///
/// Returns `None` if the tree node is not open and no content should be rendered.
#[must_use]
pub fn push(self, ui: &Ui) -> Option<TreeNodeToken> {
pub fn push<'ui>(self, ui: &Ui<'ui>) -> Option<TreeNodeToken<'ui>> {
let open = unsafe {
if self.opened_cond != Condition::Never {
sys::igSetNextItemOpen(self.opened, self.opened_cond as i32);
@ -259,13 +255,10 @@ impl<'a> TreeNode<'a> {
}
};
if open {
Some(TreeNodeToken {
ctx: if self.flags.contains(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN) {
ptr::null()
} else {
ui.ctx
},
})
Some(TreeNodeToken::new(
ui,
!self.flags.contains(TreeNodeFlags::NO_TREE_PUSH_ON_OPEN),
))
} else {
None
}
@ -274,37 +267,42 @@ impl<'a> TreeNode<'a> {
///
/// Note: the closure is not called if the tree node is not open.
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(node) = self.push(ui) {
if let Some(_node) = self.push(ui) {
f();
node.pop(ui);
}
}
}
/// Tracks a tree node that must be popped by calling `.pop()`.
/// Tracks a tree node that can be popped by calling `.pop()`, `end()`, or by dropping.
///
/// If `TreeNodeFlags::NO_TREE_PUSH_ON_OPEN` was used when this token was created, calling `.pop()`
/// is not mandatory and is a no-op.
#[must_use]
pub struct TreeNodeToken {
ctx: *const Context,
}
pub struct TreeNodeToken<'a>(core::marker::PhantomData<crate::Ui<'a>>, bool);
impl<'a> TreeNodeToken<'a> {
/// Creates a new token type. This takes a bool for the no-op variant on NO_TREE_PUSH_ON_OPEN.
pub(crate) fn new(_: &crate::Ui<'a>, execute_drop: bool) -> Self {
Self(std::marker::PhantomData, execute_drop)
}
impl TreeNodeToken {
/// Pops a tree node
#[inline]
pub fn pop(mut self, _: &Ui) {
if !self.ctx.is_null() {
self.ctx = ptr::null();
unsafe { sys::igTreePop() };
}
pub fn end(self) {
// left empty for drop
}
/// Pops a tree node
#[inline]
pub fn pop(self) {
self.end()
}
}
impl Drop for TreeNodeToken {
impl Drop for TreeNodeToken<'_> {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A TreeNodeToken was leaked. Did you call .pop()?");
if self.1 {
unsafe { sys::igTreePop() }
}
}
}

View File

@ -1,9 +1,6 @@
use std::f32;
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::sys;
use crate::window::WindowFlags;
use crate::{Id, Ui};
@ -244,7 +241,7 @@ impl<'a> ChildWindow<'a> {
/// rendered, the token must be ended by calling `.end()`.
///
/// Returns `None` if the window is not visible and no content should be rendered.
pub fn begin(self, ui: &Ui) -> Option<ChildWindowToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<ChildWindowToken<'ui>> {
if self.content_size[0] != 0.0 || self.content_size[1] != 0.0 {
unsafe { sys::igSetNextWindowContentSize(self.content_size.into()) };
}
@ -269,7 +266,7 @@ impl<'a> ChildWindow<'a> {
sys::igBeginChildID(id, self.size.into(), self.border, self.flags.bits() as i32)
};
if should_render {
Some(ChildWindowToken { ctx: ui.ctx })
Some(ChildWindowToken::new(ui))
} else {
unsafe { sys::igEndChild() };
None
@ -280,30 +277,17 @@ impl<'a> ChildWindow<'a> {
/// Note: the closure is not called if no window content is visible (e.g. window is collapsed
/// or fully clipped).
pub fn build<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(window) = self.begin(ui) {
if let Some(_window) = self.begin(ui) {
f();
window.end(ui);
}
}
}
/// Tracks a child window that must be ended by calling `.end()`
pub struct ChildWindowToken {
ctx: *const Context,
}
create_token!(
/// Tracks a child window that can be ended by calling `.end()`
/// or by dropping
pub struct ChildWindowToken<'ui>;
impl ChildWindowToken {
/// Ends a window
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEndChild() };
}
}
impl Drop for ChildWindowToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A ChildWindowToken was leaked. Did you call .end()?");
}
}
}
drop { sys::igEndChild() }
);

View File

@ -1,9 +1,7 @@
use bitflags::bitflags;
use std::f32;
use std::ptr;
use std::thread;
use crate::context::Context;
use crate::string::ImStr;
use crate::sys;
use crate::{Condition, Ui};
@ -482,7 +480,7 @@ impl<'a> Window<'a> {
///
/// Returns `None` if the window is not visible and no content should be rendered.
#[must_use]
pub fn begin(self, ui: &Ui) -> Option<WindowToken> {
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<WindowToken<'ui>> {
if self.pos_cond != Condition::Never {
unsafe {
sys::igSetNextWindowPos(
@ -528,7 +526,7 @@ impl<'a> Window<'a> {
)
};
if should_render {
Some(WindowToken { ctx: ui.ctx })
Some(WindowToken::new(ui))
} else {
unsafe { sys::igEnd() };
None
@ -539,30 +537,17 @@ 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<F: FnOnce()>(self, ui: &Ui, f: F) {
if let Some(window) = self.begin(ui) {
if let Some(_window) = self.begin(ui) {
f();
window.end(ui);
}
}
}
/// Tracks a window that must be ended by calling `.end()`
pub struct WindowToken {
ctx: *const Context,
}
create_token!(
/// Tracks a window that can be ended by calling `.end()`
/// or by dropping.
pub struct WindowToken<'ui>;
impl WindowToken {
/// Ends a window
pub fn end(mut self, _: &Ui) {
self.ctx = ptr::null();
unsafe { sys::igEnd() };
}
}
impl Drop for WindowToken {
fn drop(&mut self) {
if !self.ctx.is_null() && !thread::panicking() {
panic!("A WindowToken was leaked. Did you call .end()?");
}
}
}
drop { sys::igEnd() }
);