From 2f1cc2e9a8bd318739af9436b3f4af9509d0f337 Mon Sep 17 00:00:00 2001 From: Benjamin Adamson Date: Wed, 5 Jul 2017 18:01:29 -0700 Subject: [PATCH 1/4] Initial idea for supporting child frame's for windows. Imgui allows the user to create child frame's, and render into them. This isn't exposed currently, this is my first idea at how rust can support child frame's. Presently it's not the easiest to use, to have a window with a child frame supported, internally the imgui library must have called beginWindow() for the parent window, before beginChild() is ever called for the child frame. Doing this without causing unneeded allocations/complexity, and making the API ergonomic is something I hope to work on next / get some feedback on. --- src/child_frame.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 26 +++++++++ 2 files changed, 161 insertions(+) create mode 100644 src/child_frame.rs diff --git a/src/child_frame.rs b/src/child_frame.rs new file mode 100644 index 0000000..55c1245 --- /dev/null +++ b/src/child_frame.rs @@ -0,0 +1,135 @@ +use imgui_sys; +use ImStr; +use ImVec2; +use ImGuiWindowFlags; + +use super::{ImGuiWindowFlags_NoTitleBar, ImGuiWindowFlags_NoResize, ImGuiWindowFlags_NoMove, + ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoScrollWithMouse, + ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_AlwaysAutoResize, + ImGuiWindowFlags_ShowBorders, ImGuiWindowFlags_NoSavedSettings, + ImGuiWindowFlags_NoInputs, ImGuiWindowFlags_MenuBar, + ImGuiWindowFlags_HorizontalScrollbar, ImGuiWindowFlags_NoFocusOnAppearing, + ImGuiWindowFlags_NoBringToFrontOnFocus, ImGuiWindowFlags_AlwaysVerticalScrollbar, + ImGuiWindowFlags_AlwaysHorizontalScrollbar, ImGuiWindowFlags_AlwaysUseWindowPadding}; + +#[must_use] +pub struct ChildFrame<'p> { + name: &'p ImStr, + size: ImVec2, + flags: ImGuiWindowFlags, +} + +impl<'p> ChildFrame<'p> { + pub fn new(name: &'p ImStr, size: ImVec2) -> ChildFrame<'p> { + ChildFrame { + name: name, + size: size, + flags: ImGuiWindowFlags::empty(), + } + } + #[inline] + pub fn show_title(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoTitleBar, !value); + self + } + #[inline] + pub fn resizable(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoResize, !value); + self + } + #[inline] + pub fn movable(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoMove, !value); + self + } + #[inline] + pub fn show_scrollbar(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoScrollbar, !value); + self + } + #[inline] + pub fn show_scrollbar_with_mouse(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoScrollWithMouse, !value); + self + } + #[inline] + pub fn collapsible(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoCollapse, !value); + self + } + #[inline] + pub fn always_resizable(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_AlwaysAutoResize, value); + self + } + #[inline] + pub fn show_borders(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_ShowBorders, value); + self + } + #[inline] + pub fn save_settings(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoSavedSettings, !value); + self + } + #[inline] + pub fn input_allow(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoInputs, !value); + self + } + #[inline] + pub fn show_menu(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_MenuBar, value); + self + } + #[inline] + pub fn scrollbar_horizontal(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_HorizontalScrollbar, value); + self + } + #[inline] + pub fn focus_on_appearing(mut self, value: bool) -> Self { + self.flags.set(ImGuiWindowFlags_NoFocusOnAppearing, !value); + self + } + #[inline] + pub fn bring_to_front_on_focus(mut self, value: bool) -> Self { + self.flags.set( + ImGuiWindowFlags_NoBringToFrontOnFocus, + !value, + ); + self + } + #[inline] + pub fn always_show_vertical_scroll_bar(mut self, value: bool) -> Self { + self.flags.set( + ImGuiWindowFlags_AlwaysVerticalScrollbar, + value, + ); + self + } + #[inline] + pub fn always_show_horizontal_scroll_bar(mut self, value: bool) -> Self { + self.flags.set( + ImGuiWindowFlags_AlwaysHorizontalScrollbar, + value, + ); + self + } + #[inline] + pub fn always_use_window_padding(mut self, value: bool) -> Self { + self.flags.set( + ImGuiWindowFlags_AlwaysUseWindowPadding, + value, + ); + self + } + pub fn build(self, f: F) { + let show_border = self.flags.contains(ImGuiWindowFlags_ShowBorders); + let render_child_frame = unsafe { imgui_sys::igBeginChild(self.name.as_ptr(), self.size, show_border, self.flags) }; + if render_child_frame { + f(); + } + unsafe { imgui_sys::igEndChild() }; + } +} diff --git a/src/lib.rs b/src/lib.rs index 639cf93..9583346 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ pub use imgui_sys::{ImDrawIdx, ImDrawVert, ImGuiInputTextFlags, ImGuiInputTextFl ImGuiWindowFlags_NoSavedSettings, ImGuiWindowFlags_NoScrollWithMouse, ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoTitleBar, ImGuiWindowFlags_ShowBorders, ImVec2, ImVec4}; +pub use child_frame::ChildFrame; pub use input::{ColorEdit3, ColorEdit4, InputFloat, InputFloat2, InputFloat3, InputFloat4, InputInt, InputInt2, InputInt3, InputInt4, InputText}; pub use menus::{Menu, MenuItem}; @@ -47,6 +48,7 @@ pub use string::{ImStr, ImString}; pub use trees::{CollapsingHeader, TreeNode}; pub use window::Window; +mod child_frame; mod input; mod menus; mod plothistogram; @@ -738,3 +740,27 @@ impl<'ui> Ui<'ui> { /// ``` pub fn progress_bar<'p>(&self, fraction: f32) -> ProgressBar<'p> { ProgressBar::new(fraction) } } + +impl<'ui> Ui<'ui> { + /// Creates a child frame. Size is size of child_frame within parent window. + /// # Example + /// ```rust,no_run + /// # use imgui::*; + /// # let mut imgui = ImGui::init(); + /// # let ui = imgui.frame((0, 0), (0, 0), 0.1); + /// ui.window(im_str!("ChatWindow")) + /// .title_bar(true) + /// .scrollable(false) + /// .build(|| { + /// ui.separator(); + /// + /// ui.child_frame(im_str!("child frame"), ImVec2::new(400.0, 100.0)) + /// .show_borders(true) + /// .always_show_vertical_scroll_bar(true) + /// .show_title(true) + /// .build(|| { + /// ui.text_colored((1.0, 0.0, 0.0, 1.0), im_str!("hello mate!")); + /// }); + /// }); + pub fn child_frame<'p>(&self, name: &'p ImStr, size: ImVec2) -> ChildFrame<'p> { ChildFrame::new(name, size) } +} From 6cb0b545393c2c7df39f4a7e45815078c3fb63c1 Mon Sep 17 00:00:00 2001 From: Benjamin Adamson Date: Sat, 1 Jul 2017 11:24:46 -0700 Subject: [PATCH 2/4] Expose more functionality from imgui. Implement new_line(), calc_text_size(), and with_style_var_pushed_*() methods I needed while making a demo. I'm pretty new to Rust, but ran into this issue and thought I could maybe solve it while I was learning the library. https://github.com/Gekkio/imgui-rs/issues/39 I'm curious on your thoughts on these additions. Please LMK if you would prefer a different direction, I'm pretty new to both Rust and this library so please let me know if you have any suggestions! I'm hoping to contribute to this library moving forward, as I work on my game's UI. --- src/lib.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/style.rs | 17 +++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 src/style.rs diff --git a/src/lib.rs b/src/lib.rs index 639cf93..f1fcac8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ pub use imgui_sys::{ImDrawIdx, ImDrawVert, ImGuiInputTextFlags, ImGuiInputTextFl ImGuiInputTextFlags_ReadOnly, ImGuiKey, ImGuiSelectableFlags, ImGuiSelectableFlags_DontClosePopups, ImGuiSelectableFlags_SpanAllColumns, ImGuiSetCond, ImGuiSetCond_Always, ImGuiSetCond_Appearing, - ImGuiSetCond_FirstUseEver, ImGuiSetCond_Once, ImGuiStyle, ImGuiTreeNodeFlags, + ImGuiSetCond_FirstUseEver, ImGuiSetCond_Once, ImGuiStyle, ImGuiStyleVar, ImGuiTreeNodeFlags, ImGuiTreeNodeFlags_AllowOverlapMode, ImGuiTreeNodeFlags_Bullet, ImGuiTreeNodeFlags_CollapsingHeader, ImGuiTreeNodeFlags_DefaultOpen, ImGuiTreeNodeFlags_Framed, ImGuiTreeNodeFlags_Leaf, @@ -44,6 +44,7 @@ pub use progressbar::ProgressBar; pub use sliders::{SliderFloat, SliderFloat2, SliderFloat3, SliderFloat4, SliderInt, SliderInt2, SliderInt3, SliderInt4}; pub use string::{ImStr, ImString}; +pub use style::StyleVar; pub use trees::{CollapsingHeader, TreeNode}; pub use window::Window; @@ -54,6 +55,7 @@ mod plotlines; mod progressbar; mod sliders; mod string; +mod style; mod trees; mod window; @@ -391,6 +393,7 @@ impl<'ui> Ui<'ui> { } pub fn separator(&self) { unsafe { imgui_sys::igSeparator() }; } + pub fn new_line(&self) { unsafe { imgui_sys::igNewLine() } } pub fn same_line(&self, pos_x: f32) { unsafe { imgui_sys::igSameLine(pos_x, -1.0f32) } } pub fn same_line_spacing(&self, pos_x: f32, spacing_w: f32) { unsafe { imgui_sys::igSameLine(pos_x, spacing_w) } @@ -723,6 +726,20 @@ impl<'ui> Ui<'ui> { } } +impl<'ui> Ui<'ui> { + /// Calculate the size required for a given text string. + /// + /// hide_text_after_double_hash allows the user to insert comments into their text, using a double hash-tag prefix. + /// This is a feature of imgui. + /// + /// wrap_width allows you to request a width at which to wrap the text to a newline for the calculation. + pub fn calc_text_size(&self, text: &ImStr, hide_text_after_double_hash: bool, wrap_width: f32) -> ImVec2 { + let mut buffer = ImVec2::new(0.0, 0.0); + unsafe { imgui_sys::igCalcTextSize(&mut buffer as *mut ImVec2, text.as_ptr(), std::ptr::null(), hide_text_after_double_hash, wrap_width); } + buffer + } +} + impl<'ui> Ui<'ui> { /// Creates a progress bar. Fraction is the progress level with 0.0 = 0% and 1.0 = 100%. /// @@ -738,3 +755,70 @@ impl<'ui> Ui<'ui> { /// ``` pub fn progress_bar<'p>(&self, fraction: f32) -> ProgressBar<'p> { ProgressBar::new(fraction) } } + +impl<'ui> Ui<'ui> { + fn push_style_var(&self, style_var: StyleVar) { + use StyleVar::*; + use imgui_sys::{igPushStyleVar, igPushStyleVarVec}; + match style_var { + Alpha(v) => unsafe { igPushStyleVar(ImGuiStyleVar::Alpha, v) }, + WindowPadding(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::WindowPadding, v) }, + WindowRounding(v) => unsafe { igPushStyleVar(ImGuiStyleVar::WindowRounding, v) }, + WindowMinSize(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::WindowMinSize, v) }, + ChildWindowRounding(v) => unsafe { igPushStyleVar(ImGuiStyleVar::ChildWindowRounding, v) }, + FramePadding(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::FramePadding, v) }, + FrameRounding(v) => unsafe { igPushStyleVar(ImGuiStyleVar::FrameRounding, v) }, + ItemSpacing(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::ItemSpacing, v) }, + ItemInnerSpacing(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::ItemInnerSpacing, v) }, + IndentSpacing(v) => unsafe { igPushStyleVar(ImGuiStyleVar::IndentSpacing, v) }, + GrabMinSize(v) => unsafe { igPushStyleVar(ImGuiStyleVar::GrabMinSize, v) }, + ButtonTextAlign(v) => unsafe { igPushStyleVarVec(ImGuiStyleVar::ButtonTextAlign, v) } + } + } + + /// Runs a function after temporarily pushing a value to the style stack. + /// + /// # Example + /// ```rust,no_run + /// # use imgui::*; + /// # let mut imgui = ImGui::init(); + /// # let ui = imgui.frame((0, 0), (0, 0), 0.1); + /// ui.with_style_var(StyleVar::Alpha(0.2), || { + /// ui.text(im_str!("AB")); + /// }); + /// ui.with_style_var(StyleVar::Alpha(0.4), || { + /// ui.text(im_str!("CD")); + /// }); + /// ``` + #[inline] + pub fn with_style_var(&self, style_var: StyleVar, f: F) { + self.push_style_var(style_var); + f(); + unsafe { imgui_sys::igPopStyleVar(1) } + } + + /// Runs a function after temporarily pushing an array of values into the stack. Supporting + /// multiple is also easy since you can freely mix and match them in a safe manner. + /// + /// # Example + /// ```rust,no_run + /// # use imgui::*; + /// # let mut imgui = ImGui::init(); + /// # let ui = imgui.frame((0, 0), (0, 0), 0.1); + /// # let styles = [StyleVar::Alpha(0.2), StyleVar::WindowPadding(ImVec2::new(1.0, 1.0))]; + /// ui.with_style_vars(&styles, || { + /// ui.text(im_str!("A")); + /// ui.text(im_str!("B")); + /// ui.text(im_str!("C")); + /// ui.text(im_str!("D")); + /// }); + /// ``` + #[inline] + pub fn with_style_vars(&self, style_vars: &[StyleVar], f: F) { + for &style_var in style_vars { + self.push_style_var(style_var); + } + f(); + unsafe { imgui_sys::igPopStyleVar(style_vars.len() as i32) }; + } +} diff --git a/src/style.rs b/src/style.rs new file mode 100644 index 0000000..04c683c --- /dev/null +++ b/src/style.rs @@ -0,0 +1,17 @@ +use ImVec2; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum StyleVar { + Alpha(f32), + WindowPadding(ImVec2), + WindowRounding(f32), + WindowMinSize(ImVec2), + ChildWindowRounding(f32), + FramePadding(ImVec2), + FrameRounding(f32), + ItemSpacing(ImVec2), + ItemInnerSpacing(ImVec2), + IndentSpacing(f32), + GrabMinSize(f32), + ButtonTextAlign(ImVec2), +} From 23de1746f4be0991684b8d671f5187d3df426e7b Mon Sep 17 00:00:00 2001 From: Benjamin Adamson Date: Sat, 8 Jul 2017 02:15:59 -0700 Subject: [PATCH 3/4] Feedback implemented --- src/child_frame.rs | 26 ++++++-------------------- src/lib.rs | 3 +-- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/child_frame.rs b/src/child_frame.rs index 55c1245..8ba8ac0 100644 --- a/src/child_frame.rs +++ b/src/child_frame.rs @@ -3,11 +3,9 @@ use ImStr; use ImVec2; use ImGuiWindowFlags; -use super::{ImGuiWindowFlags_NoTitleBar, ImGuiWindowFlags_NoResize, ImGuiWindowFlags_NoMove, - ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoScrollWithMouse, +use super::{ImGuiWindowFlags_NoMove, ImGuiWindowFlags_NoScrollbar, ImGuiWindowFlags_NoScrollWithMouse, ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_AlwaysAutoResize, - ImGuiWindowFlags_ShowBorders, ImGuiWindowFlags_NoSavedSettings, - ImGuiWindowFlags_NoInputs, ImGuiWindowFlags_MenuBar, + ImGuiWindowFlags_ShowBorders, ImGuiWindowFlags_NoInputs, ImGuiWindowFlags_MenuBar, ImGuiWindowFlags_HorizontalScrollbar, ImGuiWindowFlags_NoFocusOnAppearing, ImGuiWindowFlags_NoBringToFrontOnFocus, ImGuiWindowFlags_AlwaysVerticalScrollbar, ImGuiWindowFlags_AlwaysHorizontalScrollbar, ImGuiWindowFlags_AlwaysUseWindowPadding}; @@ -28,16 +26,6 @@ impl<'p> ChildFrame<'p> { } } #[inline] - pub fn show_title(mut self, value: bool) -> Self { - self.flags.set(ImGuiWindowFlags_NoTitleBar, !value); - self - } - #[inline] - pub fn resizable(mut self, value: bool) -> Self { - self.flags.set(ImGuiWindowFlags_NoResize, !value); - self - } - #[inline] pub fn movable(mut self, value: bool) -> Self { self.flags.set(ImGuiWindowFlags_NoMove, !value); self @@ -68,11 +56,6 @@ impl<'p> ChildFrame<'p> { self } #[inline] - pub fn save_settings(mut self, value: bool) -> Self { - self.flags.set(ImGuiWindowFlags_NoSavedSettings, !value); - self - } - #[inline] pub fn input_allow(mut self, value: bool) -> Self { self.flags.set(ImGuiWindowFlags_NoInputs, !value); self @@ -125,7 +108,10 @@ impl<'p> ChildFrame<'p> { self } pub fn build(self, f: F) { - let show_border = self.flags.contains(ImGuiWindowFlags_ShowBorders); + // See issue for history. + // https://github.com/Gekkio/imgui-rs/pull/58 + let show_border = false; + let render_child_frame = unsafe { imgui_sys::igBeginChild(self.name.as_ptr(), self.size, show_border, self.flags) }; if render_child_frame { f(); diff --git a/src/lib.rs b/src/lib.rs index 9583346..46bba38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -757,10 +757,9 @@ impl<'ui> Ui<'ui> { /// ui.child_frame(im_str!("child frame"), ImVec2::new(400.0, 100.0)) /// .show_borders(true) /// .always_show_vertical_scroll_bar(true) - /// .show_title(true) /// .build(|| { /// ui.text_colored((1.0, 0.0, 0.0, 1.0), im_str!("hello mate!")); /// }); /// }); - pub fn child_frame<'p>(&self, name: &'p ImStr, size: ImVec2) -> ChildFrame<'p> { ChildFrame::new(name, size) } + pub fn child_frame<'p, S: Into>(&self, name: &'p ImStr, size: S) -> ChildFrame<'p> { ChildFrame::new(name, size.into()) } } From 0f52e197c0032f40fc6abbaa857cfe748a72ef16 Mon Sep 17 00:00:00 2001 From: Benjamin Adamson Date: Sat, 8 Jul 2017 02:20:51 -0700 Subject: [PATCH 4/4] Inline functions on the private functions, not public API. --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f1fcac8..9e9ad44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -757,6 +757,7 @@ impl<'ui> Ui<'ui> { } impl<'ui> Ui<'ui> { + #[inline] fn push_style_var(&self, style_var: StyleVar) { use StyleVar::*; use imgui_sys::{igPushStyleVar, igPushStyleVarVec}; @@ -790,7 +791,6 @@ impl<'ui> Ui<'ui> { /// ui.text(im_str!("CD")); /// }); /// ``` - #[inline] pub fn with_style_var(&self, style_var: StyleVar, f: F) { self.push_style_var(style_var); f(); @@ -813,7 +813,6 @@ impl<'ui> Ui<'ui> { /// ui.text(im_str!("D")); /// }); /// ``` - #[inline] pub fn with_style_vars(&self, style_vars: &[StyleVar], f: F) { for &style_var in style_vars { self.push_style_var(style_var);