Create windows via ui.window("title").build() [#454]

This commit is contained in:
dbr 2021-09-25 11:41:31 +10:00
parent 8fddba0143
commit 0f7cb49865
3 changed files with 79 additions and 35 deletions

View File

@ -314,6 +314,53 @@ impl<'a> Default for Id<'a> {
}
}
impl<'ui> Ui<'ui> {
/// # Windows
/// Start constructing a window.
///
/// This, like many objects in the library, uses the builder
/// pattern to set optional arguments (like window size, flags,
/// etc). Once all desired options are set, you must call either
/// [`Window::build`] or [`Window::begin`] must be called to
/// actually create the window.
///
/// # Examples
///
/// Create a window using the closure based [`Window::build`]:
/// ```no_run
/// # let mut ctx = imgui::Context::create();
/// # let ui = ctx.frame();
/// ui.window("Example Window")
/// .size([100.0, 50.0], imgui::Condition::FirstUseEver)
/// .build(|| {
/// ui.text("An example");
/// });
/// ```
pub fn window<Label: AsRef<str>>(&'ui self, name: Label) -> Window<'ui, '_, Label> {
Window::new(self, name)
}
/// Same as [`Ui::window`] but using the "token based" `.begin()` approach.
///
/// ```
/// fn example(ui: &imgui::Ui) {
/// let wt = ui.window("Example Window")
/// .size([100.0, 50.0], imgui::Condition::FirstUseEver)
/// .begin();
/// if wt.is_some() {
/// ui.text("Window is visible");
/// }
/// // Window ends where where wt is dropped,
/// // or you could call
/// wt.unwrap().end()
/// }
/// ```
pub fn child_window<Label: AsRef<str>>(&'ui self, name: Label) -> ChildWindow<'ui, Label> {
ChildWindow::new(self, name)
}
}
// Widgets: Input
impl<'ui> Ui<'ui> {
#[doc(alias = "InputText", alias = "InputTextWithHint")]

View File

@ -1,15 +1,15 @@
use std::f32;
use std::os::raw::{c_char, c_void};
use crate::sys;
use crate::window::WindowFlags;
use crate::{Id, Ui};
use crate::Ui;
/// Builder for a child window
#[derive(Copy, Clone, Debug)]
#[must_use]
pub struct ChildWindow<'a> {
id: Id<'a>,
pub struct ChildWindow<'ui, Label> {
ui: &'ui Ui<'ui>,
name: Label,
flags: WindowFlags,
size: [f32; 2],
content_size: [f32; 2],
@ -18,12 +18,13 @@ pub struct ChildWindow<'a> {
border: bool,
}
impl<'a> ChildWindow<'a> {
impl<'ui, Label: AsRef<str>> ChildWindow<'ui, Label> {
/// Creates a new child window builder with the given ID
#[doc(alias = "BeginChildID")]
pub fn new<T: Into<Id<'a>>>(id: T) -> ChildWindow<'a> {
pub fn new(ui: &'ui Ui<'ui>, name: Label) -> ChildWindow<'ui, Label> {
ChildWindow {
id: id.into(),
ui,
name,
flags: WindowFlags::empty(),
size: [0.0, 0.0],
content_size: [0.0, 0.0],
@ -245,7 +246,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<'ui>(self, ui: &Ui<'ui>) -> Option<ChildWindowToken<'ui>> {
pub fn begin(self) -> Option<ChildWindowToken<'ui>> {
if self.content_size[0] != 0.0 || self.content_size[1] != 0.0 {
unsafe { sys::igSetNextWindowContentSize(self.content_size.into()) };
}
@ -255,22 +256,16 @@ impl<'a> ChildWindow<'a> {
if self.bg_alpha.is_finite() {
unsafe { sys::igSetNextWindowBgAlpha(self.bg_alpha) };
}
let id = unsafe {
match self.id {
Id::Int(i) => sys::igGetID_Ptr(i as *const c_void),
Id::Ptr(p) => sys::igGetID_Ptr(p),
Id::Str(s) => {
let start = s.as_ptr() as *const c_char;
let end = start.add(s.len());
sys::igGetID_StrStr(start, end)
}
}
};
let should_render = unsafe {
sys::igBeginChild_ID(id, self.size.into(), self.border, self.flags.bits() as i32)
sys::igBeginChild_Str(
self.ui.scratch_txt(self.name),
self.size.into(),
self.border,
self.flags.bits() as i32,
)
};
if should_render {
Some(ChildWindowToken::new(ui))
Some(ChildWindowToken::new(self.ui))
} else {
unsafe { sys::igEndChild() };
None
@ -281,8 +276,8 @@ 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<T, F: FnOnce() -> T>(self, ui: &Ui<'_>, f: F) -> Option<T> {
self.begin(ui).map(|_window| f())
pub fn build<T, F: FnOnce() -> T>(self, f: F) -> Option<T> {
self.begin().map(|_window| f())
}
}

View File

@ -160,8 +160,9 @@ impl<'ui> Ui<'ui> {
/// Builder for a window
#[derive(Debug)]
#[must_use]
pub struct Window<'a, T> {
name: T,
pub struct Window<'ui, 'a, Label> {
ui: &'ui Ui<'ui>,
name: Label,
opened: Option<&'a mut bool>,
flags: WindowFlags,
pos: [f32; 2],
@ -177,10 +178,11 @@ pub struct Window<'a, T> {
bg_alpha: f32,
}
impl<'a, T: AsRef<str>> Window<'a, T> {
/// Creates a new window builder with the given name
pub fn new(name: T) -> Self {
impl<'ui, 'a, Label: AsRef<str>> Window<'ui, 'a, Label> {
/// Typically created via [`Ui::window`]
pub fn new(ui: &'ui Ui<'ui>, name: Label) -> Self {
Window {
ui,
name,
opened: None,
flags: WindowFlags::empty(),
@ -487,7 +489,7 @@ impl<'a, T: AsRef<str>> Window<'a, T> {
///
/// Returns `None` if the window is not visible and no content should be rendered.
#[must_use]
pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<WindowToken<'ui>> {
pub fn begin(self) -> Option<WindowToken<'ui>> {
if self.pos_cond != Condition::Never {
unsafe {
sys::igSetNextWindowPos(
@ -525,7 +527,7 @@ impl<'a, T: AsRef<str>> Window<'a, T> {
}
let should_render = unsafe {
sys::igBegin(
ui.scratch_txt(self.name),
self.ui.scratch_txt(self.name),
self.opened
.map(|x| x as *mut bool)
.unwrap_or(ptr::null_mut()),
@ -533,7 +535,7 @@ impl<'a, T: AsRef<str>> Window<'a, T> {
)
};
if should_render {
Some(WindowToken::new(ui))
Some(WindowToken::new(self.ui))
} else {
unsafe { sys::igEnd() };
None
@ -542,10 +544,10 @@ impl<'a, T: AsRef<str>> Window<'a, T> {
/// Creates a window and runs a closure to construct the contents.
/// Returns the result of the closure, if it is called.
///
/// Note: the closure is not called if no window content is visible (e.g. window is collapsed
/// or fully clipped).
pub fn build<R, F: FnOnce() -> R>(self, ui: &Ui<'_>, f: F) -> Option<R> {
self.begin(ui).map(|_window| f())
/// Note: the closure is only called if the window content is
/// visible (e.g. will not run if window is collapsed).
pub fn build<R, F: FnOnce() -> R>(self, f: F) -> Option<R> {
self.begin().map(|_window| f())
}
}