mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 13:38:35 +00:00
Require explicit ending/popping of almost all stack tokens
This is probably going to be controversial... Calling end/pop in the Drop implementation causes way too many problems, and interacts very badly with panics. There are closure-based simpler implementations of almost everything except parameter stacks that are still convenient to use. However, if we end up switching to &mut Ui in most functions (very much possible in the future!), closures will complicate matters so push/pop and begin/end pairs are still relevant.
This commit is contained in:
parent
e142f5d1b5
commit
46d099f40f
@ -26,6 +26,10 @@
|
||||
- Gfx renderer re-exports imgui and gfx
|
||||
- These functions now take/return PathBuf: log_filename, set_log_filename, ini_filename, set_logfilename
|
||||
- ID stack manipulation now uses stack tokens
|
||||
- Parameter stack pushes *must almost always be paired by a manual call to stack pop*
|
||||
- Container widget tokens *must be ended manually by calling end*.
|
||||
Closure-based function (e.g. build()) are unaffected and do this
|
||||
automatically
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
@ -283,13 +283,14 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) {
|
||||
window = window.opened(opened)
|
||||
}
|
||||
window.build(ui, || {
|
||||
let _token = ui.push_item_width(-140.0);
|
||||
ui.push_item_width(-140.0);
|
||||
ui.text(format!("dear imgui says hello. ({})", imgui::dear_imgui_version()));
|
||||
if let Some(_) = ui.menu_bar() {
|
||||
if let Some(_) = ui.menu(im_str!("Menu"), true) {
|
||||
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);
|
||||
}
|
||||
if let Some(_) = ui.menu(im_str!("Examples"), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Examples"), true) {
|
||||
MenuItem::new(im_str!("Main menu bar"))
|
||||
.build_with_ref(ui, &mut state.show_app_main_menu_bar);
|
||||
MenuItem::new(im_str!("Console"))
|
||||
@ -312,15 +313,18 @@ 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);
|
||||
}
|
||||
if let Some(_) = ui.menu(im_str!("Help"), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Help"), true) {
|
||||
MenuItem::new(im_str!("Metrics"))
|
||||
.build_with_ref(ui, &mut state.show_app_metrics);
|
||||
MenuItem::new(im_str!("Style Editor"))
|
||||
.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_bar.end(ui);
|
||||
}
|
||||
ui.spacing();
|
||||
if ui.collapsing_header(im_str!("Help")).build() {
|
||||
@ -628,7 +632,7 @@ CTRL+click on individual component to input value.\n",
|
||||
ui.popup_modal(im_str!("Delete?")).always_auto_resize(true).build(|| {
|
||||
ui.text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n");
|
||||
ui.separator();
|
||||
let _token = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0]));
|
||||
let style = ui.push_style_var(StyleVar::FramePadding([0.0, 0.0]));
|
||||
ui.checkbox(im_str!("Don't ask me next time"), &mut state.dont_ask_me_next_time);
|
||||
|
||||
if ui.button(im_str!("OK"), [120.0, 0.0]) {
|
||||
@ -638,6 +642,7 @@ 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);
|
||||
});
|
||||
|
||||
if ui.button(im_str!("Stacked modals.."), [0.0, 0.0]) {
|
||||
@ -674,11 +679,12 @@ CTRL+click on individual component to input value.\n",
|
||||
}
|
||||
|
||||
fn show_example_app_main_menu_bar<'a>(ui: &Ui<'a>, state: &mut State) {
|
||||
if let Some(_) = ui.main_menu_bar() {
|
||||
if let Some(_) = ui.menu(im_str!("File"), true) {
|
||||
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);
|
||||
}
|
||||
if let Some(_) = ui.menu(im_str!("Edit"), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Edit"), true) {
|
||||
MenuItem::new(im_str!("Undo"))
|
||||
.shortcut(im_str!("CTRL+Z"))
|
||||
.build(ui);
|
||||
@ -696,7 +702,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_bar.end(ui);
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,24 +716,27 @@ fn show_example_menu_file<'a>(ui: &Ui<'a>, state: &mut FileMenuState) {
|
||||
MenuItem::new(im_str!("Open"))
|
||||
.shortcut(im_str!("Ctrl+O"))
|
||||
.build(ui);
|
||||
if let Some(_) = ui.menu(im_str!("Open Recent"), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Open Recent"), true) {
|
||||
MenuItem::new(im_str!("fish_hat.c")).build(ui);
|
||||
MenuItem::new(im_str!("fish_hat.inl")).build(ui);
|
||||
MenuItem::new(im_str!("fish_hat.h")).build(ui);
|
||||
if let Some(_) = ui.menu(im_str!("More.."), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("More.."), true) {
|
||||
MenuItem::new(im_str!("Hello")).build(ui);
|
||||
MenuItem::new(im_str!("Sailor")).build(ui);
|
||||
if let Some(_) = ui.menu(im_str!("Recurse.."), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Recurse.."), true) {
|
||||
show_example_menu_file(ui, state);
|
||||
menu.end(ui);
|
||||
}
|
||||
menu.end(ui);
|
||||
}
|
||||
menu.end(ui);
|
||||
}
|
||||
MenuItem::new(im_str!("Save"))
|
||||
.shortcut(im_str!("Ctrl+S"))
|
||||
.build(ui);
|
||||
MenuItem::new(im_str!("Save As..")).build(ui);
|
||||
ui.separator();
|
||||
if let Some(_) = ui.menu(im_str!("Options"), true) {
|
||||
if let Some(menu) = ui.begin_menu(im_str!("Options"), true) {
|
||||
MenuItem::new(im_str!("Enabled")).build_with_ref(ui, &mut state.enabled);
|
||||
ChildWindow::new("child")
|
||||
.size([0.0, 60.0])
|
||||
@ -743,13 +754,15 @@ 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);
|
||||
}
|
||||
if let Some(_) = ui.menu(im_str!("Colors"), true) {
|
||||
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);
|
||||
}
|
||||
if let Some(_) = ui.menu(im_str!("Disabled"), false) {
|
||||
if let Some(_) = ui.begin_menu(im_str!("Disabled"), false) {
|
||||
unreachable!();
|
||||
}
|
||||
MenuItem::new(im_str!("Checked")).selected(true).build(ui);
|
||||
@ -778,7 +791,7 @@ output your content because that would create a feedback loop.",
|
||||
fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) {
|
||||
const DISTANCE: f32 = 10.0;
|
||||
let window_pos = [DISTANCE, DISTANCE];
|
||||
let _token = ui.push_style_color(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3]);
|
||||
let style = ui.push_style_color(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3]);
|
||||
Window::new(im_str!("Example: Fixed Overlay"))
|
||||
.opened(opened)
|
||||
.position(window_pos, Condition::Always)
|
||||
@ -797,7 +810,8 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) {
|
||||
"Mouse Position: ({:.1},{:.1})",
|
||||
mouse_pos[0], mouse_pos[1]
|
||||
));
|
||||
})
|
||||
});
|
||||
style.pop(ui);
|
||||
}
|
||||
|
||||
fn show_example_app_manipulating_window_title(ui: &Ui) {
|
||||
|
||||
@ -1,19 +1,32 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
|
||||
/// Represents a layout group
|
||||
pub struct GroupToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a layout group that must be ended by calling `.end()`
|
||||
#[must_use]
|
||||
pub struct GroupToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for GroupToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
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()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Cursor / Layout
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Renders a separator (generally horizontal).
|
||||
@ -67,9 +80,21 @@ impl<'ui> Ui<'ui> {
|
||||
/// Groups items together as a single item.
|
||||
///
|
||||
/// May be useful to handle the same mouse event on a group of items, for example.
|
||||
pub fn group(&self) -> GroupToken {
|
||||
///
|
||||
/// Returns a `GroupToken` that must be ended by calling `.end()`
|
||||
#[must_use]
|
||||
pub fn begin_group(&self) -> GroupToken {
|
||||
unsafe { sys::igBeginGroup() };
|
||||
GroupToken { _ui: PhantomData }
|
||||
GroupToken { ctx: self.ctx }
|
||||
}
|
||||
/// Creates a layout group and runs a closure to construct the contents.
|
||||
///
|
||||
/// May be useful to handle the same mouse event on a group of items, for example.
|
||||
pub fn group<R, F: FnOnce() -> R>(&self, f: F) -> R {
|
||||
let group = self.begin_group();
|
||||
let result = f();
|
||||
group.end(self);
|
||||
result
|
||||
}
|
||||
/// Returns the cursor position (in window coordinates)
|
||||
pub fn cursor_pos(&self) -> [f32; 2] {
|
||||
|
||||
187
src/stacks.rs
187
src/stacks.rs
@ -1,7 +1,9 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::fonts::atlas::FontId;
|
||||
use crate::internal::RawCast;
|
||||
use crate::style::{StyleColor, StyleVar};
|
||||
@ -12,6 +14,8 @@ use crate::{Id, Ui};
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Switches to the given font by pushing it to the font stack.
|
||||
///
|
||||
/// Returns a `FontStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the font atlas does not contain the given font
|
||||
@ -26,8 +30,9 @@ impl<'ui> Ui<'ui> {
|
||||
/// let my_custom_font = ctx.fonts().add_font(&font_data_sources);
|
||||
/// # let ui = ctx.frame();
|
||||
/// // During UI construction
|
||||
/// let _token = ui.push_font(my_custom_font);
|
||||
/// let font = ui.push_font(my_custom_font);
|
||||
/// ui.text("I use the custom font!");
|
||||
/// font.pop(&ui);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn push_font(&self, id: FontId) -> FontStackToken {
|
||||
@ -36,10 +41,12 @@ impl<'ui> Ui<'ui> {
|
||||
.get_font(id)
|
||||
.expect("Font atlas did not contain the given font");
|
||||
unsafe { sys::igPushFont(font.raw() as *const _ as *mut _) };
|
||||
FontStackToken { _ui: PhantomData }
|
||||
FontStackToken { ctx: self.ctx }
|
||||
}
|
||||
/// Changes a style color by pushing a change to the color stack.
|
||||
///
|
||||
/// Returns a `ColorStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@ -47,19 +54,22 @@ impl<'ui> Ui<'ui> {
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||
/// let _token = ui.push_style_color(StyleColor::Text, RED);
|
||||
/// let color = ui.push_style_color(StyleColor::Text, RED);
|
||||
/// ui.text("I'm red!");
|
||||
/// color.pop(&ui);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn push_style_color(&self, style_color: StyleColor, color: [f32; 4]) -> ColorStackToken {
|
||||
unsafe { sys::igPushStyleColor(style_color as i32, color.into()) };
|
||||
ColorStackToken {
|
||||
count: 1,
|
||||
_ui: PhantomData,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
/// Changes style colors by pushing several changes to the color stack.
|
||||
///
|
||||
/// Returns a `ColorStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@ -68,12 +78,13 @@ impl<'ui> Ui<'ui> {
|
||||
/// # let ui = ctx.frame();
|
||||
/// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||
/// const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
|
||||
/// let _token = ui.push_style_colors(&[
|
||||
/// let colors = ui.push_style_colors(&[
|
||||
/// (StyleColor::Text, RED),
|
||||
/// (StyleColor::TextDisabled, GREEN),
|
||||
/// ]);
|
||||
/// ui.text("I'm red!");
|
||||
/// ui.text_disabled("I'm green!");
|
||||
/// colors.pop(&ui);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn push_style_colors<'a, I>(&self, style_colors: I) -> ColorStackToken
|
||||
@ -87,42 +98,48 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
ColorStackToken {
|
||||
count,
|
||||
_ui: PhantomData,
|
||||
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()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// let _token = ui.push_style_var(StyleVar::Alpha(0.2));
|
||||
/// let style = ui.push_style_var(StyleVar::Alpha(0.2));
|
||||
/// ui.text("I'm transparent!");
|
||||
/// style.pop(&ui);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken {
|
||||
unsafe { push_style_var(style_var) };
|
||||
StyleStackToken {
|
||||
count: 1,
|
||||
_ui: PhantomData,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
/// Changes style variables by pushing several changes to the style stack.
|
||||
///
|
||||
/// Returns a `StyleStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use imgui::*;
|
||||
/// # let mut ctx = Context::create();
|
||||
/// # let ui = ctx.frame();
|
||||
/// let _token = ui.push_style_vars(&[
|
||||
/// let styles = ui.push_style_vars(&[
|
||||
/// StyleVar::Alpha(0.2),
|
||||
/// StyleVar::ItemSpacing([50.0, 50.0])
|
||||
/// ]);
|
||||
/// ui.text("We're transparent...");
|
||||
/// ui.text("...with large spacing as well");
|
||||
/// styles.pop(&ui);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn push_style_vars<'a, I>(&self, style_vars: I) -> StyleStackToken
|
||||
@ -136,46 +153,79 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
StyleStackToken {
|
||||
count,
|
||||
_ui: PhantomData,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a change pushed to the font stack
|
||||
pub struct FontStackToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a font pushed to the font stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct FontStackToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for FontStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
impl FontStackToken {
|
||||
/// Pops a change from the font stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopFont() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents one or more changes pushed to the color stack
|
||||
pub struct ColorStackToken<'ui> {
|
||||
count: usize,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
impl Drop for FontStackToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A FontStackToken was leaked. Did you call .pop()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ColorStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
/// Tracks one or more changes pushed to the color stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct ColorStackToken {
|
||||
count: usize,
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl ColorStackToken {
|
||||
/// Pops changes from the color stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopStyleColor(self.count as i32) };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents one or more changes pushed to the style stack
|
||||
pub struct StyleStackToken<'ui> {
|
||||
count: usize,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
impl Drop for ColorStackToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A ColorStackToken was leaked. Did you call .pop()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for StyleStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
/// Tracks one or more changes pushed to the style stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct StyleStackToken {
|
||||
count: usize,
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl StyleStackToken {
|
||||
/// Pops changes from the style stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopStyleVar(self.count as i32) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StyleStackToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A StyleStackToken was leaked. Did you call .pop()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn push_style_var(style_var: StyleVar) {
|
||||
use crate::style::StyleVar::*;
|
||||
@ -219,14 +269,15 @@ unsafe fn push_style_var(style_var: StyleVar) {
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Changes the item width by pushing a change to the item width stack.
|
||||
///
|
||||
/// Returns an `ItemWidthStackToken` that may be popped by calling `.pop()`
|
||||
///
|
||||
/// - `> 0.0`: width is `item_width` pixels
|
||||
/// - `= 0.0`: default to ~2/3 of window width
|
||||
/// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
|
||||
/// the right side)
|
||||
#[must_use]
|
||||
pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken {
|
||||
unsafe { sys::igPushItemWidth(item_width) };
|
||||
ItemWidthStackToken { _ui: PhantomData }
|
||||
ItemWidthStackToken { ctx: self.ctx }
|
||||
}
|
||||
/// Sets the width of the next item.
|
||||
///
|
||||
@ -243,16 +294,18 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
/// Changes the text wrapping position by pushing a change to the text wrapping position stack.
|
||||
///
|
||||
/// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()`
|
||||
///
|
||||
/// - `> 0.0`: wrap at `wrap_pos_x` position in window local space
|
||||
/// - `= 0.0`: wrap to end of window (or column)
|
||||
/// - `< 0.0`: no wrapping
|
||||
#[must_use]
|
||||
pub fn push_text_wrap_pos(&self, wrap_pos_x: f32) -> TextWrapPosStackToken {
|
||||
unsafe { sys::igPushTextWrapPos(wrap_pos_x) };
|
||||
TextWrapPosStackToken { _ui: PhantomData }
|
||||
TextWrapPosStackToken { ctx: self.ctx }
|
||||
}
|
||||
/// Changes an item flag by pushing a change to the item flag stack
|
||||
#[must_use]
|
||||
/// Changes an item flag by pushing a change to the item flag stack.
|
||||
///
|
||||
/// Returns a `ItemFlagsStackToken` that may be popped by calling `.pop()`
|
||||
pub fn push_item_flag(&self, item_flag: ItemFlag) -> ItemFlagsStackToken {
|
||||
use self::ItemFlag::*;
|
||||
match item_flag {
|
||||
@ -261,7 +314,7 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
ItemFlagsStackToken {
|
||||
discriminant: mem::discriminant(&item_flag),
|
||||
_ui: PhantomData,
|
||||
ctx: self.ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,36 +326,42 @@ pub enum ItemFlag {
|
||||
ButtonRepeat(bool),
|
||||
}
|
||||
|
||||
/// Represents a change pushed to the item width stack
|
||||
pub struct ItemWidthStackToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a change pushed to the item width stack
|
||||
pub struct ItemWidthStackToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ItemWidthStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
impl ItemWidthStackToken {
|
||||
/// Pops a change from the item width stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopItemWidth() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a change pushed to the text wrap position stack
|
||||
pub struct TextWrapPosStackToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a change pushed to the text wrap position stack
|
||||
pub struct TextWrapPosStackToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for TextWrapPosStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
impl TextWrapPosStackToken {
|
||||
/// Pops a change from the text wrap position stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igPopTextWrapPos() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a change pushed to the item flags stack
|
||||
pub struct ItemFlagsStackToken<'ui> {
|
||||
/// Tracks a change pushed to the item flags stack
|
||||
pub struct ItemFlagsStackToken {
|
||||
discriminant: mem::Discriminant<ItemFlag>,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ItemFlagsStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
impl ItemFlagsStackToken {
|
||||
/// Pops a change from the item flags stack
|
||||
pub fn pop(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
const ALLOW_KEYBOARD_FOCUS: ItemFlag = ItemFlag::AllowKeyboardFocus(true);
|
||||
const BUTTON_REPEAT: ItemFlag = ItemFlag::ButtonRepeat(true);
|
||||
|
||||
@ -318,7 +377,10 @@ impl<'ui> Drop for ItemFlagsStackToken<'ui> {
|
||||
|
||||
/// # ID stack
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Pushes an identifier to the ID stack
|
||||
/// Pushes an identifier to the ID stack.
|
||||
///
|
||||
/// Returns an `IdStackToken` that must be popped by calling `.pop()`
|
||||
///
|
||||
#[must_use]
|
||||
pub fn push_id<'a, I: Into<Id<'a>>>(&self, id: I) -> IdStackToken {
|
||||
let id = id.into();
|
||||
@ -334,17 +396,28 @@ impl<'ui> Ui<'ui> {
|
||||
Id::Ptr(p) => sys::igPushIDPtr(p as *const c_void),
|
||||
}
|
||||
}
|
||||
IdStackToken { _ui: PhantomData }
|
||||
IdStackToken { ctx: self.ctx }
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a change pushed to the ID stack
|
||||
pub struct IdStackToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks an ID pushed to the ID stack that must be popped by calling `.pop()`
|
||||
#[must_use]
|
||||
pub struct IdStackToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for IdStackToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
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()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use bitflags::bitflags;
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
@ -34,24 +35,24 @@ pub enum ComboBoxPreviewMode {
|
||||
}
|
||||
|
||||
bitflags!(
|
||||
/// Flags for combo boxes
|
||||
#[repr(transparent)]
|
||||
pub struct ComboBoxFlags: u32 {
|
||||
/// Align the popup toward the left by default
|
||||
const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
|
||||
/// Max ~4 items visible.
|
||||
const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
|
||||
/// Max ~8 items visible (default)
|
||||
const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
|
||||
/// Max ~20 items visible
|
||||
const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
|
||||
/// As many fitting items as possible
|
||||
const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
|
||||
/// Display on the preview box without the square arrow button
|
||||
const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
|
||||
/// Display only a square arrow button
|
||||
const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
|
||||
}
|
||||
/// Flags for combo boxes
|
||||
#[repr(transparent)]
|
||||
pub struct ComboBoxFlags: u32 {
|
||||
/// Align the popup toward the left by default
|
||||
const POPUP_ALIGN_LEFT = sys::ImGuiComboFlags_PopupAlignLeft;
|
||||
/// Max ~4 items visible.
|
||||
const HEIGHT_SMALL = sys::ImGuiComboFlags_HeightSmall;
|
||||
/// Max ~8 items visible (default)
|
||||
const HEIGHT_REGULAR = sys::ImGuiComboFlags_HeightRegular;
|
||||
/// Max ~20 items visible
|
||||
const HEIGHT_LARGE = sys::ImGuiComboFlags_HeightLarge;
|
||||
/// As many fitting items as possible
|
||||
const HEIGHT_LARGEST = sys::ImGuiComboFlags_HeightLargest;
|
||||
/// Display on the preview box without the square arrow button
|
||||
const NO_ARROW_BUTTON = sys::ImGuiComboFlags_NoArrowButton;
|
||||
/// Display only a square arrow button
|
||||
const NO_PREVIEW = sys::ImGuiComboFlags_NoPreview;
|
||||
}
|
||||
);
|
||||
|
||||
/// Builder for a combo box widget
|
||||
@ -127,10 +128,14 @@ impl<'a> ComboBox<'a> {
|
||||
);
|
||||
self
|
||||
}
|
||||
/// Builds this combo box, and starts appending to it.
|
||||
/// Creates a combo box and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(ComboBoxToken)` if the combo box is open. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the combo box is not open and no content should be rendered.
|
||||
pub fn begin<'ui>(self, _: &'ui Ui<'ui>) -> Option<ComboBoxToken<'ui>> {
|
||||
#[must_use]
|
||||
pub fn begin(self, ui: &Ui) -> Option<ComboBoxToken> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginCombo(
|
||||
self.label.as_ptr(),
|
||||
@ -139,42 +144,40 @@ impl<'a> ComboBox<'a> {
|
||||
)
|
||||
};
|
||||
if should_render {
|
||||
Some(ComboBoxToken {
|
||||
should_end: true,
|
||||
_ui: PhantomData,
|
||||
})
|
||||
Some(ComboBoxToken { ctx: ui.ctx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds the combo box.
|
||||
/// Creates a combo box and runs a closure to construct the popup contents.
|
||||
///
|
||||
/// Note: the closure is not called if the combo box is not open
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a combo box pushed to the window stack
|
||||
pub struct ComboBoxToken<'ui> {
|
||||
should_end: bool,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a combo box that must be ended by calling `.end()`
|
||||
#[must_use]
|
||||
pub struct ComboBoxToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> ComboBoxToken<'ui> {
|
||||
/// Finishes the current combo box and pops it from the window stack
|
||||
pub fn end(mut self) {
|
||||
self.should_end = false;
|
||||
impl ComboBoxToken {
|
||||
/// Ends a combo box
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEndCombo() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ComboBoxToken<'ui> {
|
||||
impl Drop for ComboBoxToken {
|
||||
fn drop(&mut self) {
|
||||
if self.should_end {
|
||||
unsafe { sys::igEndCombo() };
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A ComboBoxToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::Ui;
|
||||
@ -9,34 +10,73 @@ use crate::Ui;
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Creates and starts appending to a full-screen menu bar.
|
||||
///
|
||||
/// Returns `Some(MainMenuBarToken)` if the menu bar is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu bar is not visible and no content should be rendered.
|
||||
pub fn main_menu_bar(&self) -> Option<MainMenuBarToken> {
|
||||
#[must_use]
|
||||
pub fn begin_main_menu_bar(&self) -> Option<MainMenuBarToken> {
|
||||
if unsafe { sys::igBeginMainMenuBar() } {
|
||||
Some(MainMenuBarToken { _ui: PhantomData })
|
||||
Some(MainMenuBarToken { ctx: self.ctx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a full-screen main menu bar and runs a closure to construct the contents.
|
||||
///
|
||||
/// 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() {
|
||||
f();
|
||||
menu_bar.end(self);
|
||||
}
|
||||
}
|
||||
/// Creates and starts appending to the menu bar of the current window.
|
||||
///
|
||||
/// Returns `Some(MenuBarToken)` if the menu bar is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu bar is not visible and no content should be rendered.
|
||||
pub fn menu_bar(&self) -> Option<MenuBarToken> {
|
||||
#[must_use]
|
||||
pub fn begin_menu_bar(&self) -> Option<MenuBarToken> {
|
||||
if unsafe { sys::igBeginMenuBar() } {
|
||||
Some(MenuBarToken { _ui: PhantomData })
|
||||
Some(MenuBarToken { ctx: self.ctx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a menu bar in the current window and runs a closure to construct the contents.
|
||||
///
|
||||
/// 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() {
|
||||
f();
|
||||
menu_bar.end(self);
|
||||
}
|
||||
}
|
||||
/// Creates and starts appending to a sub-menu entry.
|
||||
///
|
||||
/// Returns `Some(MenuToken)` if the menu is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the menu is not visible and no content should be rendered.
|
||||
pub fn menu(&self, label: &ImStr, enabled: bool) -> Option<MenuToken> {
|
||||
#[must_use]
|
||||
pub fn begin_menu(&self, label: &ImStr, enabled: bool) -> Option<MenuToken> {
|
||||
if unsafe { sys::igBeginMenu(label.as_ptr(), enabled) } {
|
||||
Some(MenuToken { _ui: PhantomData })
|
||||
Some(MenuToken { ctx: self.ctx })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Creates a menu and runs a closure to construct the contents.
|
||||
///
|
||||
/// 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) {
|
||||
f();
|
||||
menu.end(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for a menu item.
|
||||
@ -111,35 +151,68 @@ impl<'a> MenuItem<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a main menu bar
|
||||
pub struct MainMenuBarToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a main menu bar that must be ended by calling `.end()`
|
||||
#[must_use]
|
||||
pub struct MainMenuBarToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> Drop for MainMenuBarToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
impl MainMenuBarToken {
|
||||
/// Ends a main menu bar
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEndMainMenuBar() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a menu bar
|
||||
pub struct MenuBarToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
impl Drop for MainMenuBarToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A MainMenuBarToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for MenuBarToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
/// 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() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a menu
|
||||
pub struct MenuToken<'ui> {
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
impl Drop for MenuBarToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A MenuBarToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for MenuToken<'ui> {
|
||||
fn drop(&mut self) {
|
||||
/// 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()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,14 +24,16 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
/// Renders simple text using the given text color
|
||||
pub fn text_colored<T: AsRef<str>>(&self, color: [f32; 4], text: T) {
|
||||
let _ = self.push_style_color(StyleColor::Text, color);
|
||||
let style = self.push_style_color(StyleColor::Text, color);
|
||||
self.text(text);
|
||||
style.pop(self);
|
||||
}
|
||||
/// 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 _ = self.push_style_color(StyleColor::Text, color);
|
||||
let style = self.push_style_color(StyleColor::Text, color);
|
||||
self.text(text);
|
||||
style.pop(self);
|
||||
}
|
||||
/// Renders text wrapped to the end of window (or column)
|
||||
pub fn text_wrapped(&self, text: &ImStr) {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use std::f32;
|
||||
use std::marker::PhantomData;
|
||||
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};
|
||||
@ -228,8 +230,13 @@ impl<'a> ChildWindow<'a> {
|
||||
self.flags |= WindowFlags::NO_INPUTS;
|
||||
self
|
||||
}
|
||||
/// Builds this window, pushes it to the window stack, and starts appending to it
|
||||
pub fn begin<'ui>(self, _: &'ui Ui<'ui>) -> ChildWindowToken<'ui> {
|
||||
/// Creates a child window and starts append to it.
|
||||
///
|
||||
/// Returns `Some(ChildWindowToken)` if the window is visible. After content has been
|
||||
/// 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> {
|
||||
if self.content_size[0] != 0.0 || self.content_size[1] != 0.0 {
|
||||
unsafe { sys::igSetNextWindowContentSize(self.content_size.into()) };
|
||||
}
|
||||
@ -253,45 +260,42 @@ impl<'a> ChildWindow<'a> {
|
||||
let should_render = unsafe {
|
||||
sys::igBeginChildID(id, self.size.into(), self.border, self.flags.bits() as i32)
|
||||
};
|
||||
ChildWindowToken {
|
||||
should_render,
|
||||
should_end: true,
|
||||
_ui: PhantomData,
|
||||
if should_render {
|
||||
Some(ChildWindowToken { ctx: ui.ctx })
|
||||
} else {
|
||||
unsafe { sys::igEndChild() };
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds this child window using the given closure to create the window content.
|
||||
/// Creates a child window and runs a closure to construct the contents.
|
||||
///
|
||||
/// 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) {
|
||||
let window = self.begin(ui);
|
||||
if window.should_render {
|
||||
if let Some(window) = self.begin(ui) {
|
||||
f();
|
||||
window.end(ui);
|
||||
}
|
||||
window.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a child window pushed to the window stack
|
||||
pub struct ChildWindowToken<'ui> {
|
||||
/// True, if the child window contents should be rendered
|
||||
pub should_render: bool,
|
||||
should_end: bool,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a child window that must be ended by calling `.end()`
|
||||
pub struct ChildWindowToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> ChildWindowToken<'ui> {
|
||||
/// Finishes the current child window and pops it from the window stack
|
||||
pub fn end(mut self) {
|
||||
self.should_end = false;
|
||||
impl ChildWindowToken {
|
||||
/// Ends a window
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEndChild() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for ChildWindowToken<'ui> {
|
||||
impl Drop for ChildWindowToken {
|
||||
fn drop(&mut self) {
|
||||
if self.should_end {
|
||||
unsafe { sys::igEndChild() };
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A ChildWindowToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use bitflags::bitflags;
|
||||
use std::f32;
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::string::ImStr;
|
||||
use crate::sys;
|
||||
use crate::{Condition, Ui};
|
||||
@ -470,8 +471,14 @@ impl<'a> Window<'a> {
|
||||
self.flags |= WindowFlags::NO_INPUTS;
|
||||
self
|
||||
}
|
||||
/// Builds this window, pushes it to the window stack, and starts appending to it
|
||||
pub fn begin<'ui>(self, _: &'ui Ui<'ui>) -> WindowToken<'ui> {
|
||||
/// Creates a window and starts appending to it.
|
||||
///
|
||||
/// Returns `Some(WindowToken)` if the window is visible. After content has been
|
||||
/// rendered, the token must be ended by calling `.end()`.
|
||||
///
|
||||
/// Returns `None` if the window is not visible and no content should be rendered.
|
||||
#[must_use]
|
||||
pub fn begin(self, ui: &Ui) -> Option<WindowToken> {
|
||||
if self.pos_cond != Condition::Never {
|
||||
unsafe {
|
||||
sys::igSetNextWindowPos(
|
||||
@ -516,45 +523,42 @@ impl<'a> Window<'a> {
|
||||
self.flags.bits() as i32,
|
||||
)
|
||||
};
|
||||
WindowToken {
|
||||
should_render,
|
||||
should_end: true,
|
||||
_ui: PhantomData,
|
||||
if should_render {
|
||||
Some(WindowToken { ctx: ui.ctx })
|
||||
} else {
|
||||
unsafe { sys::igEnd() };
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Builds this window using the given closure to create the window content.
|
||||
/// Creates a window and runs a closure to construct the contents.
|
||||
///
|
||||
/// 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) {
|
||||
let window = self.begin(ui);
|
||||
if window.should_render {
|
||||
if let Some(window) = self.begin(ui) {
|
||||
f();
|
||||
window.end(ui);
|
||||
}
|
||||
window.end();
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a window pushed to the window stack
|
||||
pub struct WindowToken<'ui> {
|
||||
/// True, if the window contents should be rendered
|
||||
pub should_render: bool,
|
||||
should_end: bool,
|
||||
_ui: PhantomData<&'ui Ui<'ui>>,
|
||||
/// Tracks a window that must be ended by calling `.end()`
|
||||
pub struct WindowToken {
|
||||
ctx: *const Context,
|
||||
}
|
||||
|
||||
impl<'ui> WindowToken<'ui> {
|
||||
/// Finishes the current window and pops it from the window stack
|
||||
pub fn end(mut self) {
|
||||
self.should_end = false;
|
||||
impl WindowToken {
|
||||
/// Ends a window
|
||||
pub fn end(mut self, _: &Ui) {
|
||||
self.ctx = ptr::null();
|
||||
unsafe { sys::igEnd() };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Drop for WindowToken<'ui> {
|
||||
impl Drop for WindowToken {
|
||||
fn drop(&mut self) {
|
||||
if self.should_end {
|
||||
unsafe { sys::igEnd() };
|
||||
if !self.ctx.is_null() && !thread::panicking() {
|
||||
panic!("A WindowToken was leaked. Did you call .end()?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user