dear_imgui/
stacks.rs

1#![allow(
2    clippy::cast_possible_truncation,
3    clippy::cast_sign_loss,
4    clippy::as_conversions
5)]
6use crate::Ui;
7use crate::fonts::FontId;
8use crate::style::{StyleColor, StyleVar};
9use crate::sys;
10
11/// # Parameter stacks (shared)
12impl Ui {
13    /// Switches to the given font by pushing it to the font stack.
14    ///
15    /// Returns a `FontStackToken` that must be popped by calling `.pop()`
16    ///
17    /// # Panics
18    ///
19    /// Panics if the font atlas does not contain the given font
20    ///
21    /// # Examples
22    ///
23    /// ```no_run
24    /// # use dear_imgui::*;
25    /// # let mut ctx = Context::create();
26    /// # let font_data_sources = [];
27    /// // At initialization time
28    /// let my_custom_font = ctx.fonts().add_font(&font_data_sources);
29    /// # let ui = ctx.frame();
30    /// // During UI construction
31    /// let font = ui.push_font(my_custom_font);
32    /// ui.text("I use the custom font!");
33    /// font.pop();
34    /// ```
35    #[doc(alias = "PushFont")]
36    pub fn push_font(&self, id: FontId) -> FontStackToken<'_> {
37        // For now, we'll use a simplified approach without full validation
38        // TODO: Add proper FontAtlas integration for validation
39        let font_ptr = id.0 as *mut sys::ImFont;
40        unsafe { sys::igPushFont(font_ptr, 0.0) };
41        FontStackToken::new(self)
42    }
43
44    /// Changes a style color by pushing a change to the color stack.
45    ///
46    /// Returns a `ColorStackToken` that must be popped by calling `.pop()`
47    ///
48    /// # Examples
49    ///
50    /// ```no_run
51    /// # use dear_imgui::*;
52    /// # let mut ctx = Context::create();
53    /// # let ui = ctx.frame();
54    /// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
55    /// let color = ui.push_style_color(StyleColor::Text, RED);
56    /// ui.text("I'm red!");
57    /// color.pop();
58    /// ```
59    #[doc(alias = "PushStyleColor")]
60    pub fn push_style_color(
61        &self,
62        style_color: StyleColor,
63        color: impl Into<[f32; 4]>,
64    ) -> ColorStackToken<'_> {
65        let color_array = color.into();
66        unsafe {
67            sys::igPushStyleColor_Vec4(
68                style_color as i32,
69                sys::ImVec4 {
70                    x: color_array[0],
71                    y: color_array[1],
72                    z: color_array[2],
73                    w: color_array[3],
74                },
75            )
76        };
77        ColorStackToken::new(self)
78    }
79
80    /// Changes a style variable by pushing a change to the style stack.
81    ///
82    /// Returns a `StyleStackToken` that can be popped by calling `.end()`
83    /// or by allowing to drop.
84    ///
85    /// # Examples
86    ///
87    /// ```no_run
88    /// # use dear_imgui::*;
89    /// # let mut ctx = Context::create();
90    /// # let ui = ctx.frame();
91    /// let style = ui.push_style_var(StyleVar::Alpha(0.2));
92    /// ui.text("I'm transparent!");
93    /// style.pop();
94    /// ```
95    #[doc(alias = "PushStyleVar")]
96    pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> {
97        unsafe { push_style_var(style_var) };
98        StyleStackToken::new(self)
99    }
100}
101
102create_token!(
103    /// Tracks a font pushed to the font stack that can be popped by calling `.end()`
104    /// or by dropping.
105    pub struct FontStackToken<'ui>;
106
107    /// Pops a change from the font stack.
108    drop { unsafe { sys::igPopFont() } }
109);
110
111impl FontStackToken<'_> {
112    /// Pops a change from the font stack.
113    pub fn pop(self) {
114        self.end()
115    }
116}
117
118create_token!(
119    /// Tracks a color pushed to the color stack that can be popped by calling `.end()`
120    /// or by dropping.
121    pub struct ColorStackToken<'ui>;
122
123    /// Pops a change from the color stack.
124    drop { unsafe { sys::igPopStyleColor(1) } }
125);
126
127impl ColorStackToken<'_> {
128    /// Pops a change from the color stack.
129    pub fn pop(self) {
130        self.end()
131    }
132}
133
134create_token!(
135    /// Tracks a style pushed to the style stack that can be popped by calling `.end()`
136    /// or by dropping.
137    pub struct StyleStackToken<'ui>;
138
139    /// Pops a change from the style stack.
140    drop { unsafe { sys::igPopStyleVar(1) } }
141);
142
143impl StyleStackToken<'_> {
144    /// Pops a change from the style stack.
145    pub fn pop(self) {
146        self.end()
147    }
148}
149
150/// Helper function to push style variables
151unsafe fn push_style_var(style_var: StyleVar) {
152    use StyleVar::*;
153    match style_var {
154        Alpha(v) => unsafe { sys::igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v) },
155        DisabledAlpha(v) => unsafe {
156            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DisabledAlpha as i32, v)
157        },
158        WindowPadding(v) => {
159            let p: [f32; 2] = v;
160            let vec = sys::ImVec2 { x: p[0], y: p[1] };
161            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, vec) }
162        }
163        WindowRounding(v) => unsafe {
164            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v)
165        },
166        WindowBorderSize(v) => unsafe {
167            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v)
168        },
169        WindowMinSize(v) => {
170            let p: [f32; 2] = v;
171            let vec = sys::ImVec2 { x: p[0], y: p[1] };
172            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, vec) }
173        }
174        WindowTitleAlign(v) => {
175            let p: [f32; 2] = v;
176            let vec = sys::ImVec2 { x: p[0], y: p[1] };
177            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, vec) }
178        }
179        ChildRounding(v) => unsafe {
180            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v)
181        },
182        ChildBorderSize(v) => unsafe {
183            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v)
184        },
185        PopupRounding(v) => unsafe {
186            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v)
187        },
188        PopupBorderSize(v) => unsafe {
189            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v)
190        },
191        FramePadding(v) => {
192            let p: [f32; 2] = v;
193            let vec = sys::ImVec2 { x: p[0], y: p[1] };
194            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, vec) }
195        }
196        FrameRounding(v) => unsafe {
197            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v)
198        },
199        FrameBorderSize(v) => unsafe {
200            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v)
201        },
202        ItemSpacing(v) => {
203            let p: [f32; 2] = v;
204            let vec = sys::ImVec2 { x: p[0], y: p[1] };
205            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, vec) }
206        }
207        ItemInnerSpacing(v) => {
208            let p: [f32; 2] = v;
209            let vec = sys::ImVec2 { x: p[0], y: p[1] };
210            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, vec) }
211        }
212        IndentSpacing(v) => unsafe {
213            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v)
214        },
215        CellPadding(v) => {
216            let p: [f32; 2] = v;
217            let vec = sys::ImVec2 { x: p[0], y: p[1] };
218            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_CellPadding as i32, vec) }
219        }
220        ScrollbarSize(v) => unsafe {
221            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v)
222        },
223        ScrollbarRounding(v) => unsafe {
224            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v)
225        },
226        GrabMinSize(v) => unsafe {
227            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v)
228        },
229        GrabRounding(v) => unsafe {
230            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v)
231        },
232        TabRounding(v) => unsafe {
233            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v)
234        },
235        ButtonTextAlign(v) => {
236            let p: [f32; 2] = v;
237            let vec = sys::ImVec2 { x: p[0], y: p[1] };
238            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, vec) }
239        }
240        SelectableTextAlign(v) => {
241            let p: [f32; 2] = v;
242            let vec = sys::ImVec2 { x: p[0], y: p[1] };
243            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, vec) }
244        }
245    }
246}
247
248/// # Parameter stacks (current window)
249impl Ui {
250    /// Changes the item width by pushing a change to the item width stack.
251    ///
252    /// Returns an `ItemWidthStackToken`. The pushed width item is popped when either
253    /// `ItemWidthStackToken` goes out of scope, or `.end()` is called.
254    ///
255    /// - `> 0.0`: width is `item_width` pixels
256    /// - `= 0.0`: default to ~2/3 of window width
257    /// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
258    ///   the right side)
259    #[doc(alias = "PushItemWidth")]
260    pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken<'_> {
261        unsafe { sys::igPushItemWidth(item_width) };
262        ItemWidthStackToken::new(self)
263    }
264
265    /// Sets the width of the next item(s) to be the same as the width of the given text.
266    ///
267    /// Returns an `ItemWidthStackToken`. The pushed width item is popped when either
268    /// `ItemWidthStackToken` goes out of scope, or `.end()` is called.
269    #[doc(alias = "PushItemWidth")]
270    pub fn push_item_width_text(&self, text: impl AsRef<str>) -> ItemWidthStackToken<'_> {
271        let text_width = {
272            let text_ptr = self.scratch_txt(text);
273            let mut out = sys::ImVec2 { x: 0.0, y: 0.0 };
274            unsafe { sys::igCalcTextSize(&mut out, text_ptr, std::ptr::null(), false, -1.0) };
275            out.x
276        };
277        self.push_item_width(text_width)
278    }
279
280    /// Sets the position where text will wrap around.
281    ///
282    /// Returns a `TextWrapPosStackToken`. The pushed wrap position is popped when either
283    /// `TextWrapPosStackToken` goes out of scope, or `.end()` is called.
284    ///
285    /// - `wrap_pos_x < 0.0`: no wrapping
286    /// - `wrap_pos_x = 0.0`: wrap to end of window (or column)
287    /// - `wrap_pos_x > 0.0`: wrap at `wrap_pos_x` position in window local space
288    #[doc(alias = "PushTextWrapPos")]
289    pub fn push_text_wrap_pos(&self, wrap_pos_x: f32) -> TextWrapPosStackToken<'_> {
290        unsafe { sys::igPushTextWrapPos(wrap_pos_x) };
291        TextWrapPosStackToken::new(self)
292    }
293}
294
295create_token!(
296    /// Tracks a change made with [`Ui::push_item_width`] that can be popped
297    /// by calling [`ItemWidthStackToken::end`] or dropping.
298    pub struct ItemWidthStackToken<'ui>;
299
300    /// Pops an item width change made with [`Ui::push_item_width`].
301    #[doc(alias = "PopItemWidth")]
302    drop { unsafe { sys::igPopItemWidth() } }
303);
304
305create_token!(
306    /// Tracks a change made with [`Ui::push_text_wrap_pos`] that can be popped
307    /// by calling [`TextWrapPosStackToken::end`] or dropping.
308    pub struct TextWrapPosStackToken<'ui>;
309
310    /// Pops a text wrap position change made with [`Ui::push_text_wrap_pos`].
311    #[doc(alias = "PopTextWrapPos")]
312    drop { unsafe { sys::igPopTextWrapPos() } }
313);
314
315/// # ID stack
316impl Ui {
317    /// Pushes an identifier to the ID stack.
318    ///
319    /// Returns an `IdStackToken` that can be popped by calling `.end()`
320    /// or by dropping manually.
321    ///
322    /// # Examples
323    /// Dear ImGui uses labels to uniquely identify widgets. For a good explanation, see this part of the [Dear ImGui FAQ][faq]
324    ///
325    /// [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
326    ///
327    /// In `dear-imgui` the same applies, we can manually specify labels with the `##` syntax:
328    ///
329    /// ```no_run
330    /// # let mut imgui = dear_imgui::Context::create();
331    /// # let ui = imgui.frame();
332    ///
333    /// ui.button("Click##button1");
334    /// ui.button("Click##button2");
335    /// ```
336    ///
337    /// But sometimes we want to create widgets in a loop, or we want to avoid
338    /// having to manually give each widget a unique label. In these cases, we can
339    /// push an ID to the ID stack:
340    ///
341    /// ```no_run
342    /// # let mut imgui = dear_imgui::Context::create();
343    /// # let ui = imgui.frame();
344    ///
345    /// for i in 0..10 {
346    ///     let _id = ui.push_id(i);
347    ///     ui.button("Click");
348    /// }
349    /// ```
350    #[doc(alias = "PushID")]
351    pub fn push_id<'a, T: Into<Id<'a>>>(&self, id: T) -> IdStackToken<'_> {
352        let id = id.into();
353        unsafe {
354            match id {
355                Id::Int(i) => sys::igPushID_Int(i),
356                Id::Str(s) => sys::igPushID_Str(self.scratch_txt(s)),
357                Id::Ptr(p) => sys::igPushID_Ptr(p),
358            }
359        }
360        IdStackToken::new(self)
361    }
362}
363
364create_token!(
365    /// Tracks an ID pushed to the ID stack that can be popped by calling `.pop()`
366    /// or by dropping. See [`crate::Ui::push_id`] for more details.
367    pub struct IdStackToken<'ui>;
368
369    /// Pops a change from the ID stack
370    drop { unsafe { sys::igPopID() } }
371);
372
373impl IdStackToken<'_> {
374    /// Pops a change from the ID stack.
375    pub fn pop(self) {
376        self.end()
377    }
378}
379
380// ============================================================================
381// Focus scope stack
382// ============================================================================
383
384create_token!(
385    /// Tracks a pushed focus scope, popped on drop.
386    pub struct FocusScopeToken<'ui>;
387
388    /// Pops a focus scope.
389    #[doc(alias = "PopFocusScope")]
390    drop { unsafe { sys::igPopFocusScope() } }
391);
392
393impl Ui {
394    /// Push a focus scope (affects e.g. navigation focus allocation).
395    ///
396    /// Returns a `FocusScopeToken` which will pop the focus scope when dropped.
397    #[doc(alias = "PushFocusScope")]
398    pub fn push_focus_scope(&self, id: sys::ImGuiID) -> FocusScopeToken<'_> {
399        unsafe { sys::igPushFocusScope(id) };
400        FocusScopeToken::new(self)
401    }
402}
403
404/// Represents an identifier that can be pushed to the ID stack
405#[derive(Copy, Clone, Debug)]
406pub enum Id<'a> {
407    /// Integer identifier
408    Int(i32),
409    /// String identifier
410    Str(&'a str),
411    /// Pointer identifier
412    Ptr(*const std::ffi::c_void),
413}
414
415impl From<i32> for Id<'_> {
416    fn from(i: i32) -> Self {
417        Id::Int(i)
418    }
419}
420
421impl From<usize> for Id<'_> {
422    fn from(i: usize) -> Self {
423        Id::Int(i as i32)
424    }
425}
426
427impl<'a> From<&'a str> for Id<'a> {
428    fn from(s: &'a str) -> Self {
429        Id::Str(s)
430    }
431}
432
433impl<'a> From<&'a String> for Id<'a> {
434    fn from(s: &'a String) -> Self {
435        Id::Str(s.as_str())
436    }
437}
438
439impl<T> From<*const T> for Id<'_> {
440    fn from(p: *const T) -> Self {
441        Id::Ptr(p as *const std::ffi::c_void)
442    }
443}
444
445impl<T> From<*mut T> for Id<'_> {
446    fn from(p: *mut T) -> Self {
447        Id::Ptr(p as *const std::ffi::c_void)
448    }
449}