use crate::fonts::atlas::FontId; use crate::internal::RawCast; use crate::math::MintVec4; use crate::style::{StyleColor, StyleVar}; use crate::sys; use crate::Ui; use std::mem; use std::os::raw::c_char; /// # Parameter stacks (shared) impl 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 /// /// # 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 font = ui.push_font(my_custom_font); /// ui.text("I use the custom font!"); /// font.pop(); /// ``` #[doc(alias = "PushFont")] 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::new(self) } /// 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 /// # use imgui::*; /// # let mut ctx = Context::create(); /// # let ui = ctx.frame(); /// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; /// let color = ui.push_style_color(StyleColor::Text, RED); /// ui.text("I'm red!"); /// color.pop(); /// ``` #[doc(alias = "PushStyleColorVec4")] pub fn push_style_color( &self, style_color: StyleColor, color: impl Into, ) -> ColorStackToken<'_> { unsafe { sys::igPushStyleColor_Vec4(style_color as i32, color.into().into()) }; ColorStackToken::new(self) } /// Changes a style variable by pushing a change to the style stack. /// /// Returns a `StyleStackToken` that can be popped by calling `.end()` /// or by allowing to drop. /// /// # Examples /// /// ```no_run /// # use imgui::*; /// # let mut ctx = Context::create(); /// # let ui = ctx.frame(); /// let style = ui.push_style_var(StyleVar::Alpha(0.2)); /// ui.text("I'm transparent!"); /// style.pop(); /// ``` #[doc(alias = "PushStyleVar")] pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> { unsafe { push_style_var(style_var) }; StyleStackToken::new(self) } } create_token!( /// Tracks a font pushed to the font stack that can be popped by calling `.end()` /// or by dropping. pub struct FontStackToken<'ui>; /// 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() } } 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() } } 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() } } #[inline] unsafe fn push_style_var(style_var: StyleVar) { use crate::style::StyleVar::*; use crate::sys::{igPushStyleVar_Float, igPushStyleVar_Vec2}; match style_var { Alpha(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v), WindowPadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, v.into()), WindowRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v), WindowBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v), WindowMinSize(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, v.into()), WindowTitleAlign(v) => { igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, v.into()) } ChildRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v), ChildBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v), PopupRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v), PopupBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v), FramePadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, v.into()), FrameRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v), FrameBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v), ItemSpacing(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, v.into()), ItemInnerSpacing(v) => { igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, v.into()) } IndentSpacing(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v), ScrollbarSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v), ScrollbarRounding(v) => { igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v) } GrabMinSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v), GrabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v), TabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v), ButtonTextAlign(v) => { igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, v.into()) } SelectableTextAlign(v) => { igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, v.into()) } CellPadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_CellPadding as i32, v.into()), } } /// # Parameter stacks (current window) impl Ui { /// Changes the item width by pushing a change to the item width stack. /// /// Returns an `ItemWidthStackToken`. The pushed width item is popped when either /// `ItemWidthStackToken` goes out of scope, or `.end()` is called. /// /// - `> 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) #[doc(alias = "PushItemWith")] pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken<'_> { unsafe { sys::igPushItemWidth(item_width) }; ItemWidthStackToken::new(self) } /// Sets the width of the next item. /// /// - `> 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) #[doc(alias = "SetNextItemWidth")] pub fn set_next_item_width(&self, item_width: f32) { unsafe { sys::igSetNextItemWidth(item_width) }; } /// Returns the width of the item given the pushed settings and the current cursor position. /// /// This is NOT necessarily the width of last item. #[doc(alias = "CalcItemWidth")] pub fn calc_item_width(&self) -> f32 { unsafe { sys::igCalcItemWidth() } } /// Makes the text wrap at the end of window/column (which is generally the default), by /// pushing a change to the text wrapping position stack. /// /// This is the same as calling [push_text_wrap_pos_with_pos](Self::push_text_wrap_pos_with_pos) /// with `wrap_pos_x` set to 0.0. /// /// Returns a `TextWrapPosStackToken`. The pushed position item is popped when either /// `TextWrapPosStackToken` goes out of scope, or `.end()` is called. #[doc(alias = "PushTextWrapPos")] pub fn push_text_wrap_pos(&self) -> TextWrapPosStackToken<'_> { self.push_text_wrap_pos_with_pos(0.0) } /// Changes the text wrapping position by pushing a change to the text wrapping position stack. /// /// Returns a `TextWrapPosStackToken`. The pushed position item is popped when either /// `TextWrapPosStackToken` goes out of scope, or `.end()` is called. /// /// - `> 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 #[doc(alias = "PushTextWrapPos")] pub fn push_text_wrap_pos_with_pos(&self, wrap_pos_x: f32) -> TextWrapPosStackToken<'_> { unsafe { sys::igPushTextWrapPos(wrap_pos_x) }; TextWrapPosStackToken::new(self) } /// Tab stop enable. /// Allow focusing using TAB/Shift-TAB, enabled by default but you can /// disable it for certain widgets /// /// Returns a [PushAllowKeyboardFocusToken] that should be dropped. #[doc(alias = "PushAllowKeyboardFocus")] pub fn push_allow_keyboard_focus(&self, allow: bool) -> PushAllowKeyboardFocusToken<'_> { unsafe { sys::igPushAllowKeyboardFocus(allow) }; PushAllowKeyboardFocusToken::new(self) } /// In 'repeat' mode, button_x functions return repeated true in a typematic /// manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). /// Note that you can call IsItemActive() after any Button() to tell if the /// button is held in the current frame. /// /// Returns a [PushButtonRepeatToken] that should be dropped. #[doc(alias = "PushAllowKeyboardFocus")] pub fn push_button_repeat(&self, allow: bool) -> PushButtonRepeatToken<'_> { unsafe { sys::igPushButtonRepeat(allow) }; PushButtonRepeatToken::new(self) } /// Changes an item flag by pushing a change to the item flag stack. /// /// Returns a `ItemFlagsStackToken` that may be popped by calling `.pop()` /// /// ## Deprecated /// /// This was deprecated in `0.9.0` because it isn't part of the dear imgui design, /// and supporting it required a manual implementation of its drop token. /// /// Instead, just use [`push_allow_keyboard_focus`] and [`push_button_repeat`]. /// /// [`push_allow_keyboard_focus`]: Self::push_allow_keyboard_focus /// [`push_button_repeat`]: Self::push_button_repeat #[deprecated( since = "0.9.0", note = "use `push_allow_keyboard_focus` or `push_button_repeat` instead" )] 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::new(self, item_flag) } } /// A temporary change in item flags #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ItemFlag { AllowKeyboardFocus(bool), ButtonRepeat(bool), } create_token!( pub struct ItemWidthStackToken<'ui>; #[doc(alias = "PopItemWidth")] drop { sys::igPopItemWidth() } ); create_token!( pub struct TextWrapPosStackToken<'ui>; #[doc(alias = "PopTextWrapPos")] drop { sys::igPopTextWrapPos() } ); create_token!( pub struct PushAllowKeyboardFocusToken<'ui>; #[doc(alias = "PopAllowKeyboardFocus")] drop { sys::igPopAllowKeyboardFocus() } ); create_token!( pub struct PushButtonRepeatToken<'ui>; #[doc(alias = "PopButtonRepeat")] drop { sys::igPopButtonRepeat() } ); /// Tracks a change pushed to the item flags stack. /// /// The "item flags" stack was a concept invented in imgui-rs that doesn't have an /// ImGui equivalent. We're phasing these out to make imgui-rs feel simpler to use. #[must_use] pub struct ItemFlagsStackToken<'a>( std::marker::PhantomData<&'a Ui>, mem::Discriminant, ); impl<'a> ItemFlagsStackToken<'a> { /// Creates a new token type. pub(crate) fn new(_: &'a crate::Ui, kind: ItemFlag) -> Self { Self(std::marker::PhantomData, mem::discriminant(&kind)) } #[inline] pub fn end(self) { // left empty for drop } } impl Drop for ItemFlagsStackToken<'_> { fn drop(&mut self) { unsafe { if self.1 == mem::discriminant(&ItemFlag::AllowKeyboardFocus(true)) { sys::igPopAllowKeyboardFocus(); } else if self.1 == mem::discriminant(&ItemFlag::ButtonRepeat(true)) { sys::igPopButtonRepeat(); } else { unreachable!(); } } } } create_token!( /// Tracks an ID pushed to the ID stack that can be popped by calling `.pop()` /// or by dropping. See [`crate::Ui::push_id`] for more details. 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 { /// Pushes an identifier to the ID stack. /// /// Returns an `IdStackToken` that can be popped by calling `.end()` /// or by dropping manually. /// /// # Examples /// Dear ImGui uses labels to uniquely identify widgets. For a good explaination, see this part of the [Dear ImGui FAQ][faq] /// /// [faq]: https://github.com/ocornut/imgui/blob/v1.84.2/docs/FAQ.md#q-why-is-my-widget-not-reacting-when-i-click-on-it /// /// In `imgui-rs` the same applies, we can manually specify labels with the `##` syntax: /// /// ```no_run /// # let mut imgui = imgui::Context::create(); /// # let ui = imgui.frame(); /// /// ui.button("Click##button1"); /// ui.button("Click##button2"); /// ``` /// /// Here `Click` is the label used for both buttons, and everything after `##` is used as the identifier. /// /// However when you either have many items (say, created in a loop), we can use our loop number as an item in the "ID stack": /// /// ```no_run /// # let mut imgui = imgui::Context::create(); /// # let ui = imgui.frame(); /// /// ui.window("Example").build(|| { /// // The window adds "Example" to the token stack. /// for num in 0..10 { /// // And now we add the loop number to the stack too, /// // to make our buttons unique within this window. /// let _id = ui.push_id_usize(num); /// if ui.button("Click!") { /// println!("Button {} clicked", num); /// } /// } /// }); /// ``` /// /// We don't have to use numbers - strings also work: /// /// ```no_run /// # let mut imgui = imgui::Context::create(); /// # let ui = imgui.frame(); /// /// fn callback1(ui: &imgui::Ui) { /// if ui.button("Click") { /// println!("First button clicked") /// } /// } /// /// fn callback2(ui: &imgui::Ui) { /// if ui.button("Click") { /// println!("Second button clicked") /// } /// } /// /// ui.window("Example") /// .build(||{ /// { /// // Since we don't know what callback1 might do, we create /// // a unique ID stack by pushing (a hash of) "first" to the ID stack: /// let _id1 = ui.push_id("first"); /// callback1(&ui); /// } /// { /// // Same for second callback. If we didn't do this, clicking the "Click" button /// // would trigger both println statements! /// let id2 = ui.push_id("second"); /// callback2(&ui); /// // Here we manually end the scope. Typically letting it drop is neater /// // but sometimes it's useful to end the scope earlier /// id2.end(); /// ui.text("We can do other things, outside of the id2 scope"); /// } /// }); /// ``` #[doc(alias = "PushId")] pub fn push_id(&self, s: impl AsRef) -> IdStackToken<'_> { unsafe { let s = s.as_ref(); let start = s.as_ptr() as *const c_char; let end = start.add(s.len()); sys::igPushID_StrStr(start, end) } IdStackToken::new(self) } /// Pushes a `usize` to the ID stack. /// /// Returns an `IdStackToken` that can be popped by calling `.end()` /// or by dropping manually. /// /// See [push_id] for more information. /// /// [push_id]: Self::push_id #[doc(alias = "PushId")] pub fn push_id_usize(&self, id: usize) -> IdStackToken<'_> { unsafe { sys::igPushID_Ptr(id as *const _) } IdStackToken::new(self) } /// Pushes an `i32` to the ID stack. /// /// Returns an `IdStackToken` that can be popped by calling `.end()` /// or by dropping manually. /// /// See [push_id] for more information. /// /// [push_id]: Self::push_id #[doc(alias = "PushId")] pub fn push_id_int(&self, id: i32) -> IdStackToken<'_> { unsafe { sys::igPushID_Int(id) } IdStackToken::new(self) } /// Pushes a `ptr` to the ID stack. /// /// Returns an `IdStackToken` that can be popped by calling `.end()` /// or by dropping manually. /// /// See [push_id] for more information. /// /// [push_id]: Self::push_id #[doc(alias = "PushId")] pub fn push_id_ptr(&self, value: &T) -> IdStackToken<'_> { unsafe { sys::igPushID_Ptr(value as *const T as *const _) } IdStackToken::new(self) } }