diff --git a/imgui-examples/examples/test_window_impl.rs b/imgui-examples/examples/test_window_impl.rs index c34a9f5..c19327a 100644 --- a/imgui-examples/examples/test_window_impl.rs +++ b/imgui-examples/examples/test_window_impl.rs @@ -284,7 +284,7 @@ fn show_test_window(ui: &Ui, state: &mut State, opened: &mut bool) { window = window.opened(opened) } window.build(|| { - ui.push_item_width(-140.0); + let _token = ui.push_item_width(-140.0); ui.text(format!("dear imgui says hello. ({})", imgui::dear_imgui_version())); ui.menu_bar(|| { ui.menu(im_str!("Menu")).build(|| { @@ -651,17 +651,16 @@ 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(); - ui.with_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); + let _token = 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]) { - ui.close_current_popup(); - } - ui.same_line(0.0); - if ui.button(im_str!("Cancel"), [120.0, 0.0]) { - ui.close_current_popup(); - } - }); + if ui.button(im_str!("OK"), [120.0, 0.0]) { + ui.close_current_popup(); + } + ui.same_line(0.0); + if ui.button(im_str!("Cancel"), [120.0, 0.0]) { + ui.close_current_popup(); + } }); if ui.button(im_str!("Stacked modals.."), [0.0, 0.0]) { @@ -802,7 +801,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]; - ui.with_color_var(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3], || { + let _token = ui.push_style_color(StyleColor::WindowBg, [0.0, 0.0, 0.0, 0.3]); ui.window(im_str!("Example: Fixed Overlay")) .opened(opened) .position(window_pos, Condition::Always) @@ -820,7 +819,6 @@ fn show_example_app_fixed_overlay(ui: &Ui, opened: &mut bool) { mouse_pos[0], mouse_pos[1] )); }) - }) } fn show_example_app_manipulating_window_title(ui: &Ui) { diff --git a/src/legacy.rs b/src/legacy.rs index bb6929d..d20da85 100644 --- a/src/legacy.rs +++ b/src/legacy.rs @@ -7,6 +7,7 @@ use std::str; use crate::input::keyboard::Key; use crate::input::mouse::MouseButton; use crate::internal::RawCast; +use crate::style::{StyleColor, StyleVar}; use crate::{Context, Ui}; bitflags!( @@ -666,3 +667,56 @@ pub fn get_version() -> &'static str { str::from_utf8_unchecked(bytes) } } + +impl<'ui> Ui<'ui> { + #[deprecated(since = "0.1.0", note = "use Ui::push_style_color instead")] + pub fn with_color_var(&self, style_color: StyleColor, color: [f32; 4], f: F) -> T + where + F: FnOnce() -> T, + { + let _token = self.push_style_color(style_color, color); + f() + } + #[deprecated(since = "0.1.0", note = "use Ui::push_style_colors instead")] + pub fn with_color_vars<'a, T, F, I>(&self, style_colors: I, f: F) -> T + where + F: FnOnce() -> T, + I: IntoIterator, + { + let _token = self.push_style_colors(style_colors); + f() + } + #[deprecated(since = "0.1.0", note = "use Ui::push_style_var instead")] + pub fn with_style_var(&self, style_var: StyleVar, f: F) -> T + where + F: FnOnce() -> T, + { + let _token = self.push_style_var(style_var); + f() + } + #[deprecated(since = "0.1.0", note = "use Ui::push_style_vars instead")] + pub fn with_style_vars<'a, T, F, I>(&self, style_vars: I, f: F) -> T + where + F: FnOnce() -> T, + I: IntoIterator, + { + let _token = self.push_style_vars(style_vars); + f() + } + #[deprecated(since = "0.1.0", note = "use Ui::push_item_width instead")] + pub fn with_item_width(&self, item_width: f32, f: F) -> T + where + F: FnOnce() -> T, + { + let _token = self.push_item_width(item_width); + f() + } + #[deprecated(since = "0.1.0", note = "use Ui::push_text_wrap_pos instead")] + pub fn with_text_wrap_pos(&self, wrap_pos_x: f32, f: F) -> T + where + F: FnOnce() -> T, + { + let _token = self.push_text_wrap_pos(wrap_pos_x); + f() + } +} diff --git a/src/lib.rs b/src/lib.rs index 43df9eb..6857927 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,8 @@ pub use self::sliders::{ SliderFloat, SliderFloat2, SliderFloat3, SliderFloat4, SliderInt, SliderInt2, SliderInt3, SliderInt4, }; -pub use self::string::{ImStr, ImString}; +pub use self::stacks::*; +pub use self::string::*; pub use self::style::*; pub use self::trees::{CollapsingHeader, TreeNode}; pub use self::window::Window; @@ -68,11 +69,13 @@ mod popup_modal; mod progressbar; mod render; mod sliders; +mod stacks; mod string; mod style; #[cfg(test)] mod test; mod trees; +mod widget; mod window; mod window_draw_list; @@ -122,6 +125,12 @@ impl<'ui> Ui<'ui> { }, } } + pub fn clone_style(&self) -> Style { + *self.ctx.style() + } + pub fn style_color(&self, style_color: StyleColor) -> [f32; 4] { + self.ctx.style()[style_color] + } pub fn get_time(&self) -> f64 { unsafe { sys::igGetTime() } } @@ -201,29 +210,6 @@ impl<'ui> Ui<'ui> { // Layout impl<'ui> Ui<'ui> { - /// Pushes a value to the item width stack. - pub fn push_item_width(&self, width: f32) { - unsafe { sys::igPushItemWidth(width) } - } - - /// Pops a value from the item width stack. - /// - /// # Aborts - /// The current process is aborted if the item width stack is empty. - pub fn pop_item_width(&self) { - unsafe { sys::igPopItemWidth() } - } - - /// Runs a function after temporarily pushing a value to the item width stack. - pub fn with_item_width(&self, width: f32, f: F) - where - F: FnOnce(), - { - self.push_item_width(width); - f(); - self.pop_item_width(); - } - pub fn separator(&self) { unsafe { sys::igSeparator() }; } @@ -390,57 +376,11 @@ impl<'ui> Ui<'ui> { // Widgets impl<'ui> Ui<'ui> { - pub fn text>(&self, text: T) { - let s = text.as_ref(); - unsafe { - let start = s.as_ptr(); - let end = start.add(s.len()); - sys::igTextUnformatted(start as *const c_char, end as *const c_char); - } - } - pub fn text_colored<'p>(&self, col: [f32; 4], text: &'p ImStr) { - unsafe { - sys::igTextColored(col.into(), fmt_ptr(), text.as_ptr()); - } - } - pub fn text_disabled<'p>(&self, text: &'p ImStr) { - unsafe { - sys::igTextDisabled(fmt_ptr(), text.as_ptr()); - } - } - pub fn text_wrapped<'p>(&self, text: &'p ImStr) { - unsafe { - sys::igTextWrapped(fmt_ptr(), text.as_ptr()); - } - } - /// Set word-wrapping for `text_*()` commands. - /// - `< 0.0`: no wrapping; - /// - `= 0.0`: wrap to end of window (or column); - /// - `> 0.0`: wrap at `wrap_pos_x` position in window local space - pub fn with_text_wrap_pos(&self, wrap_pos_x: f32, f: F) { - unsafe { - sys::igPushTextWrapPos(wrap_pos_x); - } - f(); - unsafe { - sys::igPopTextWrapPos(); - } - } - pub fn label_text<'p>(&self, label: &'p ImStr, text: &'p ImStr) { - unsafe { - sys::igLabelText(label.as_ptr(), fmt_ptr(), text.as_ptr()); - } - } pub fn bullet(&self) { unsafe { sys::igBullet(); } } - pub fn bullet_text<'p>(&self, text: &'p ImStr) { - unsafe { - sys::igBulletText(fmt_ptr(), text.as_ptr()); - } - } pub fn button(&self, label: &ImStr, size: [f32; 2]) -> bool { unsafe { sys::igButton(label.as_ptr(), size.into()) } } @@ -1017,188 +957,6 @@ impl<'ui> Ui<'ui> { } } -impl<'ui> Ui<'ui> { - /// Runs a function after temporarily pushing a value to the style stack. - /// - /// # Example - /// ```rust,no_run - /// # use imgui::*; - /// # let mut imgui = Context::create(); - /// # let ui = imgui.frame(); - /// ui.with_style_var(StyleVar::Alpha(0.2), || { - /// ui.text(im_str!("AB")); - /// }); - /// ``` - pub fn with_style_var(&self, style_var: StyleVar, f: F) { - self.push_style_var(style_var); - f(); - unsafe { 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 = Context::create(); - /// # let ui = imgui.frame(); - /// # let styles = [StyleVar::Alpha(0.2), StyleVar::WindowPadding([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")); - /// }); - /// ``` - 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 { sys::igPopStyleVar(style_vars.len() as i32) }; - } - - #[inline] - fn push_style_var(&self, style_var: StyleVar) { - use crate::style::StyleVar::*; - use crate::sys::{igPushStyleVarFloat, igPushStyleVarVec2}; - match style_var { - Alpha(v) => unsafe { igPushStyleVarFloat(sys::ImGuiStyleVar_Alpha as i32, v) }, - WindowPadding(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_WindowPadding as i32, v.into()) - }, - WindowRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_WindowRounding as i32, v) - }, - WindowBorderSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_WindowBorderSize as i32, v) - }, - WindowMinSize(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_WindowMinSize as i32, v.into()) - }, - WindowTitleAlign(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, v.into()) - }, - ChildRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_ChildRounding as i32, v) - }, - ChildBorderSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_ChildBorderSize as i32, v) - }, - PopupRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_PopupRounding as i32, v) - }, - PopupBorderSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_PopupBorderSize as i32, v) - }, - FramePadding(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_FramePadding as i32, v.into()) - }, - FrameRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_FrameRounding as i32, v) - }, - FrameBorderSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_FrameBorderSize as i32, v) - }, - ItemSpacing(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_ItemSpacing as i32, v.into()) - }, - ItemInnerSpacing(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, v.into()) - }, - IndentSpacing(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_IndentSpacing as i32, v) - }, - ScrollbarSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_ScrollbarSize as i32, v) - }, - ScrollbarRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_ScrollbarRounding as i32, v) - }, - GrabMinSize(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_GrabMinSize as i32, v) - }, - GrabRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_GrabRounding as i32, v) - }, - TabRounding(v) => unsafe { - igPushStyleVarFloat(sys::ImGuiStyleVar_TabRounding as i32, v) - }, - ButtonTextAlign(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, v.into()) - }, - SelectableTextAlign(v) => unsafe { - igPushStyleVarVec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, v.into()) - }, - } - } -} - -impl<'ui> Ui<'ui> { - /// Runs a function after temporarily pushing a value to the color stack. - /// - /// # Example - /// ```rust,no_run - /// # use imgui::*; - /// # let mut imgui = Context::create(); - /// # let ui = imgui.frame(); - /// ui.with_color_var(StyleColor::Text, [1.0, 0.0, 0.0, 1.0], || { - /// ui.text_wrapped(im_str!("AB")); - /// }); - /// ``` - pub fn with_color_var(&self, var: StyleColor, color: [f32; 4], f: F) { - unsafe { - sys::igPushStyleColor(var as _, color.into()); - } - f(); - unsafe { - sys::igPopStyleColor(1); - } - } - - /// Runs a function after temporarily pushing an array of values to the color stack. - /// - /// # Example - /// ```rust,no_run - /// # use imgui::*; - /// # let mut imgui = Context::create(); - /// # let ui = imgui.frame(); - /// let red = [1.0, 0.0, 0.0, 1.0]; - /// let green = [0.0, 1.0, 0.0, 1.0]; - /// # let vars = [(StyleColor::Text, red), (StyleColor::TextDisabled, green)]; - /// ui.with_color_vars(&vars, || { - /// ui.text_wrapped(im_str!("AB")); - /// }); - /// ``` - pub fn with_color_vars(&self, color_vars: &[(StyleColor, [f32; 4])], f: F) { - for &(color_var, color) in color_vars { - unsafe { - sys::igPushStyleColor(color_var as _, color.into()); - } - } - f(); - unsafe { sys::igPopStyleColor(color_vars.len() as i32) }; - } -} - -impl<'ui> Ui<'ui> { - /// Runs a function after temporarily pushing an array of values to the - /// style and color stack. - pub fn with_style_and_color_vars( - &self, - style_vars: &[StyleVar], - color_vars: &[(StyleColor, [f32; 4])], - f: F, - ) where - F: FnOnce(), - { - self.with_style_vars(style_vars, || { - self.with_color_vars(color_vars, f); - }); - } -} - /// # Utilities impl<'ui> Ui<'ui> { /// Returns `true` if the last item is being hovered by the mouse. diff --git a/src/stacks.rs b/src/stacks.rs new file mode 100644 index 0000000..7a37f42 --- /dev/null +++ b/src/stacks.rs @@ -0,0 +1,303 @@ +use std::marker::PhantomData; +use std::mem; + +use crate::fonts::atlas::FontId; +use crate::internal::RawCast; +use crate::style::{StyleColor, StyleVar}; +use crate::sys; +use crate::Ui; + +/// # Parameter stacks (shared) +impl<'ui> Ui<'ui> { + /// Switches to the given font by pushing it to the font stack. + /// + /// # Panics + /// + /// Panics if the font atlas does not contain the given font + /// + /// # Examples + /// + /// ```no_run + /// # use imgui::*; + /// # let mut ctx = Context::create(); + /// # let font_data_sources = []; + /// // At initialization time + /// 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); + /// ui.text("I use the custom font!"); + /// ``` + #[must_use] + 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 { _ui: PhantomData } + } + /// Changes a style color by pushing a change to the color stack. + /// + /// # Examples + /// + /// ```no_run + /// # use imgui::*; + /// # 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); + /// ui.text("I'm red!"); + /// ``` + #[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, + } + } + /// Changes style colors by pushing several changes to the color stack. + /// + /// # Examples + /// + /// ```no_run + /// # use imgui::*; + /// # let mut ctx = Context::create(); + /// # 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(&[ + /// (StyleColor::Text, RED), + /// (StyleColor::TextDisabled, GREEN), + /// ]); + /// ui.text("I'm red!"); + /// ui.text_disabled("I'm green!"); + /// ``` + #[must_use] + pub fn push_style_colors<'a, I>(&self, style_colors: I) -> ColorStackToken + where + I: IntoIterator, + { + let mut count = 0; + for &(style_color, color) in style_colors { + unsafe { sys::igPushStyleColor(style_color as i32, color.into()) }; + count += 1; + } + ColorStackToken { + count, + _ui: PhantomData, + } + } + /// Changes a style variable by pushing a change to the style stack. + /// + /// # Examples + /// + /// ```no_run + /// # use imgui::*; + /// # let mut ctx = Context::create(); + /// # let ui = ctx.frame(); + /// let _token = ui.push_style_var(StyleVar::Alpha(0.2)); + /// ui.text("I'm transparent!"); + /// ``` + #[must_use] + pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken { + unsafe { push_style_var(style_var) }; + StyleStackToken { + count: 1, + _ui: PhantomData, + } + } + /// Changes style variables by pushing several changes to the style stack. + /// + /// # Examples + /// + /// ```no_run + /// # use imgui::*; + /// # let mut ctx = Context::create(); + /// # let ui = ctx.frame(); + /// let _token = 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"); + /// ``` + #[must_use] + pub fn push_style_vars<'a, I>(&self, style_vars: I) -> StyleStackToken + where + I: IntoIterator, + { + let mut count = 0; + for &style_var in style_vars { + unsafe { push_style_var(style_var) }; + count += 1; + } + StyleStackToken { + count, + _ui: PhantomData, + } + } +} + +/// Represents a change pushed to the font stack +pub struct FontStackToken<'ui> { + _ui: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Drop for FontStackToken<'ui> { + fn drop(&mut self) { + 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<'ui> Drop for ColorStackToken<'ui> { + fn drop(&mut self) { + 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<'ui> Drop for StyleStackToken<'ui> { + fn drop(&mut self) { + unsafe { sys::igPopStyleVar(self.count as i32) }; + } +} + +#[inline] +unsafe fn push_style_var(style_var: StyleVar) { + use crate::style::StyleVar::*; + use crate::sys::{igPushStyleVarFloat, igPushStyleVarVec2}; + match style_var { + Alpha(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_Alpha as i32, v), + WindowPadding(v) => igPushStyleVarVec2(sys::ImGuiStyleVar_WindowPadding as i32, v.into()), + WindowRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_WindowRounding as i32, v), + WindowBorderSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_WindowBorderSize as i32, v), + WindowMinSize(v) => igPushStyleVarVec2(sys::ImGuiStyleVar_WindowMinSize as i32, v.into()), + WindowTitleAlign(v) => { + igPushStyleVarVec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, v.into()) + } + ChildRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_ChildRounding as i32, v), + ChildBorderSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_ChildBorderSize as i32, v), + PopupRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_PopupRounding as i32, v), + PopupBorderSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_PopupBorderSize as i32, v), + FramePadding(v) => igPushStyleVarVec2(sys::ImGuiStyleVar_FramePadding as i32, v.into()), + FrameRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_FrameRounding as i32, v), + FrameBorderSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_FrameBorderSize as i32, v), + ItemSpacing(v) => igPushStyleVarVec2(sys::ImGuiStyleVar_ItemSpacing as i32, v.into()), + ItemInnerSpacing(v) => { + igPushStyleVarVec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, v.into()) + } + IndentSpacing(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_IndentSpacing as i32, v), + ScrollbarSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_ScrollbarSize as i32, v), + ScrollbarRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_ScrollbarRounding as i32, v), + GrabMinSize(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_GrabMinSize as i32, v), + GrabRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_GrabRounding as i32, v), + TabRounding(v) => igPushStyleVarFloat(sys::ImGuiStyleVar_TabRounding as i32, v), + ButtonTextAlign(v) => { + igPushStyleVarVec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, v.into()) + } + SelectableTextAlign(v) => { + igPushStyleVarVec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, v.into()) + } + } +} + +/// # Parameter stacks (current window) +impl<'ui> Ui<'ui> { + /// Changes the item width by pushing a change to the item width stack. + /// + /// - `> 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 } + } + /// Changes the text wrapping position by pushing a change to the text wrapping position stack. + /// + /// - `> 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 } + } + /// Changes an item flag by pushing a change to the item flag stack + #[must_use] + pub fn push_item_flag(&self, item_flag: ItemFlag) -> ItemFlagsStackToken { + use self::ItemFlag::*; + match item_flag { + AllowKeyboardFocus(v) => unsafe { sys::igPushAllowKeyboardFocus(v) }, + ButtonRepeat(v) => unsafe { sys::igPushButtonRepeat(v) }, + } + ItemFlagsStackToken { + discriminant: mem::discriminant(&item_flag), + _ui: PhantomData, + } + } +} + +/// A temporary change in item flags +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ItemFlag { + AllowKeyboardFocus(bool), + ButtonRepeat(bool), +} + +/// Represents a change pushed to the item width stack +pub struct ItemWidthStackToken<'ui> { + _ui: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Drop for ItemWidthStackToken<'ui> { + fn drop(&mut self) { + unsafe { sys::igPopItemWidth() }; + } +} + +/// Represents a change pushed to the text wrap position stack +pub struct TextWrapPosStackToken<'ui> { + _ui: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Drop for TextWrapPosStackToken<'ui> { + fn drop(&mut self) { + unsafe { sys::igPopTextWrapPos() }; + } +} + +/// Represents a change pushed to the item flags stack +pub struct ItemFlagsStackToken<'ui> { + discriminant: mem::Discriminant, + _ui: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Drop for ItemFlagsStackToken<'ui> { + fn drop(&mut self) { + const ALLOW_KEYBOARD_FOCUS: ItemFlag = ItemFlag::AllowKeyboardFocus(true); + const BUTTON_REPEAT: ItemFlag = ItemFlag::ButtonRepeat(true); + + if self.discriminant == mem::discriminant(&ALLOW_KEYBOARD_FOCUS) { + unsafe { sys::igPopAllowKeyboardFocus() }; + } else if self.discriminant == mem::discriminant(&BUTTON_REPEAT) { + unsafe { sys::igPopButtonRepeat() }; + } else { + unreachable!(); + } + } +} diff --git a/src/widget/mod.rs b/src/widget/mod.rs new file mode 100644 index 0000000..481c63a --- /dev/null +++ b/src/widget/mod.rs @@ -0,0 +1 @@ +pub mod text; diff --git a/src/widget/text.rs b/src/widget/text.rs new file mode 100644 index 0000000..a76877d --- /dev/null +++ b/src/widget/text.rs @@ -0,0 +1,44 @@ +use std::os::raw::c_char; + +use crate::string::ImStr; +use crate::style::StyleColor; +use crate::Ui; + +static FMT: &'static [u8] = b"%s\0"; + +#[inline] +fn fmt_ptr() -> *const c_char { + FMT.as_ptr() as *const c_char +} + +impl<'ui> Ui<'ui> { + /// Renders simple text + pub fn text>(&self, text: T) { + let s = text.as_ref(); + unsafe { + let start = s.as_ptr(); + let end = start.add(s.len()); + sys::igTextUnformatted(start as *const c_char, end as *const c_char); + } + } + /// Renders simple text using the given text color + pub fn text_colored>(&self, color: [f32; 4], text: T) { + let _ = self.push_style_color(StyleColor::Text, color); + self.text(text); + } + /// Renders simple text using `StyleColor::TextDisabled` color + pub fn text_disabled>(&self, text: T) { + let color = self.style_color(StyleColor::TextDisabled); + let _ = self.push_style_color(StyleColor::Text, color); + self.text(text); + } + pub fn text_wrapped(&self, text: &ImStr) { + unsafe { sys::igTextWrapped(fmt_ptr(), text.as_ptr()) } + } + pub fn label_text(&self, label: &ImStr, text: &ImStr) { + unsafe { sys::igLabelText(label.as_ptr(), fmt_ptr(), text.as_ptr()) } + } + pub fn bullet_text(&self, text: &ImStr) { + unsafe { sys::igBulletText(fmt_ptr(), text.as_ptr()) } + } +}