Skip to main content

dear_imgui_rs/stacks/
style.rs

1use crate::style::{
2    StyleColor, StyleVar, StyleVarVec2, validate_style_color, validate_style_var,
3    validate_style_var_component,
4};
5use crate::{Ui, sys};
6
7impl Ui {
8    /// Changes a style color by pushing a change to the color stack.
9    ///
10    /// Returns a `ColorStackToken` that must be popped by calling `.pop()`
11    ///
12    /// # Examples
13    ///
14    /// ```no_run
15    /// # use dear_imgui_rs::*;
16    /// # let mut ctx = Context::create();
17    /// # let ui = ctx.frame();
18    /// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
19    /// let color = ui.push_style_color(StyleColor::Text, RED);
20    /// ui.text("I'm red!");
21    /// color.pop();
22    /// ```
23    #[doc(alias = "PushStyleColor")]
24    pub fn push_style_color(
25        &self,
26        style_color: StyleColor,
27        color: impl Into<[f32; 4]>,
28    ) -> ColorStackToken<'_> {
29        let color_array = color.into();
30        validate_style_color("Ui::push_style_color()", "color", color_array);
31        self.run_with_bound_context(|| unsafe {
32            sys::igPushStyleColor_Vec4(
33                style_color as i32,
34                sys::ImVec4 {
35                    x: color_array[0],
36                    y: color_array[1],
37                    z: color_array[2],
38                    w: color_array[3],
39                },
40            )
41        });
42        ColorStackToken::new(self)
43    }
44
45    /// Changes a style variable by pushing a change to the style stack.
46    ///
47    /// Returns a `StyleStackToken` that can be popped by calling `.end()`
48    /// or by allowing to drop.
49    ///
50    /// # Examples
51    ///
52    /// ```no_run
53    /// # use dear_imgui_rs::*;
54    /// # let mut ctx = Context::create();
55    /// # let ui = ctx.frame();
56    /// let style = ui.push_style_var(StyleVar::Alpha(0.2));
57    /// ui.text("I'm transparent!");
58    /// style.pop();
59    /// ```
60    #[doc(alias = "PushStyleVar")]
61    pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> {
62        validate_style_var("Ui::push_style_var()", style_var);
63        self.run_with_bound_context(|| unsafe { push_style_var(style_var) });
64        StyleStackToken::new(self)
65    }
66
67    /// Overrides the X component of a two-component style variable.
68    #[doc(alias = "PushStyleVarX")]
69    pub fn push_style_var_x(&self, style_var: StyleVarVec2, value: f32) -> StyleStackToken<'_> {
70        validate_style_var_component("Ui::push_style_var_x()", style_var, value);
71        self.run_with_bound_context(|| unsafe { sys::igPushStyleVarX(style_var.raw(), value) });
72        StyleStackToken::new(self)
73    }
74
75    /// Overrides the Y component of a two-component style variable.
76    #[doc(alias = "PushStyleVarY")]
77    pub fn push_style_var_y(&self, style_var: StyleVarVec2, value: f32) -> StyleStackToken<'_> {
78        validate_style_var_component("Ui::push_style_var_y()", style_var, value);
79        self.run_with_bound_context(|| unsafe { sys::igPushStyleVarY(style_var.raw(), value) });
80        StyleStackToken::new(self)
81    }
82}
83
84create_token!(
85    /// Tracks a color pushed to the color stack that can be popped by calling `.end()`
86    /// or by dropping.
87    #[doc(alias = "PopStyleColor")]
88    pub struct ColorStackToken<'ui>;
89
90    /// Pops a change from the color stack.
91    drop { unsafe { sys::igPopStyleColor(1) } }
92);
93
94impl ColorStackToken<'_> {
95    /// Pops a change from the color stack.
96    pub fn pop(self) {
97        self.end()
98    }
99}
100
101create_token!(
102    /// Tracks a style pushed to the style stack that can be popped by calling `.end()`
103    /// or by dropping.
104    #[doc(alias = "PopStyleVar")]
105    pub struct StyleStackToken<'ui>;
106
107    /// Pops a change from the style stack.
108    drop { unsafe { sys::igPopStyleVar(1) } }
109);
110
111impl StyleStackToken<'_> {
112    /// Pops a change from the style stack.
113    pub fn pop(self) {
114        self.end()
115    }
116}
117
118/// Helper function to push style variables
119unsafe fn push_style_var(style_var: StyleVar) {
120    use StyleVar::*;
121    match style_var {
122        Alpha(v) => unsafe { sys::igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v) },
123        DisabledAlpha(v) => unsafe {
124            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DisabledAlpha as i32, v)
125        },
126        WindowPadding(v) => {
127            let p: [f32; 2] = v;
128            let vec = sys::ImVec2 { x: p[0], y: p[1] };
129            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, vec) }
130        }
131        WindowRounding(v) => unsafe {
132            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v)
133        },
134        WindowBorderSize(v) => unsafe {
135            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v)
136        },
137        WindowMinSize(v) => {
138            let p: [f32; 2] = v;
139            let vec = sys::ImVec2 { x: p[0], y: p[1] };
140            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, vec) }
141        }
142        WindowTitleAlign(v) => {
143            let p: [f32; 2] = v;
144            let vec = sys::ImVec2 { x: p[0], y: p[1] };
145            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, vec) }
146        }
147        ChildRounding(v) => unsafe {
148            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v)
149        },
150        ChildBorderSize(v) => unsafe {
151            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v)
152        },
153        PopupRounding(v) => unsafe {
154            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v)
155        },
156        PopupBorderSize(v) => unsafe {
157            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v)
158        },
159        FramePadding(v) => {
160            let p: [f32; 2] = v;
161            let vec = sys::ImVec2 { x: p[0], y: p[1] };
162            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, vec) }
163        }
164        FrameRounding(v) => unsafe {
165            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v)
166        },
167        FrameBorderSize(v) => unsafe {
168            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v)
169        },
170        ItemSpacing(v) => {
171            let p: [f32; 2] = v;
172            let vec = sys::ImVec2 { x: p[0], y: p[1] };
173            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, vec) }
174        }
175        ItemInnerSpacing(v) => {
176            let p: [f32; 2] = v;
177            let vec = sys::ImVec2 { x: p[0], y: p[1] };
178            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, vec) }
179        }
180        IndentSpacing(v) => unsafe {
181            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v)
182        },
183        CellPadding(v) => {
184            let p: [f32; 2] = v;
185            let vec = sys::ImVec2 { x: p[0], y: p[1] };
186            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_CellPadding as i32, vec) }
187        }
188        ScrollbarSize(v) => unsafe {
189            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v)
190        },
191        ScrollbarRounding(v) => unsafe {
192            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v)
193        },
194        ScrollbarPadding(v) => unsafe {
195            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarPadding as i32, v)
196        },
197        GrabMinSize(v) => unsafe {
198            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v)
199        },
200        GrabRounding(v) => unsafe {
201            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v)
202        },
203        ImageRounding(v) => unsafe {
204            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ImageRounding as i32, v)
205        },
206        ImageBorderSize(v) => unsafe {
207            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ImageBorderSize as i32, v)
208        },
209        TabRounding(v) => unsafe {
210            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v)
211        },
212        TabBorderSize(v) => unsafe {
213            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBorderSize as i32, v)
214        },
215        TabMinWidthBase(v) => unsafe {
216            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabMinWidthBase as i32, v)
217        },
218        TabMinWidthShrink(v) => unsafe {
219            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabMinWidthShrink as i32, v)
220        },
221        TabBarBorderSize(v) => unsafe {
222            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBarBorderSize as i32, v)
223        },
224        TabBarOverlineSize(v) => unsafe {
225            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBarOverlineSize as i32, v)
226        },
227        TableAngledHeadersAngle(v) => unsafe {
228            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TableAngledHeadersAngle as i32, v)
229        },
230        TableAngledHeadersTextAlign(v) => {
231            let p: [f32; 2] = v;
232            let vec = sys::ImVec2 { x: p[0], y: p[1] };
233            unsafe {
234                sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_TableAngledHeadersTextAlign as i32, vec)
235            }
236        }
237        TreeLinesSize(v) => unsafe {
238            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TreeLinesSize as i32, v)
239        },
240        TreeLinesRounding(v) => unsafe {
241            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TreeLinesRounding as i32, v)
242        },
243        MenuItemRounding(v) => unsafe {
244            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_MenuItemRounding as i32, v)
245        },
246        SelectableRounding(v) => unsafe {
247            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SelectableRounding as i32, v)
248        },
249        DragDropTargetRounding(v) => unsafe {
250            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DragDropTargetRounding as i32, v)
251        },
252        ButtonTextAlign(v) => {
253            let p: [f32; 2] = v;
254            let vec = sys::ImVec2 { x: p[0], y: p[1] };
255            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, vec) }
256        }
257        SelectableTextAlign(v) => {
258            let p: [f32; 2] = v;
259            let vec = sys::ImVec2 { x: p[0], y: p[1] };
260            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, vec) }
261        }
262        SeparatorSize(v) => unsafe {
263            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SeparatorSize as i32, v)
264        },
265        SeparatorTextBorderSize(v) => unsafe {
266            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SeparatorTextBorderSize as i32, v)
267        },
268        SeparatorTextAlign(v) => {
269            let p: [f32; 2] = v;
270            let vec = sys::ImVec2 { x: p[0], y: p[1] };
271            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SeparatorTextAlign as i32, vec) }
272        }
273        SeparatorTextPadding(v) => {
274            let p: [f32; 2] = v;
275            let vec = sys::ImVec2 { x: p[0], y: p[1] };
276            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SeparatorTextPadding as i32, vec) }
277        }
278        DockingSeparatorSize(v) => unsafe {
279            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DockingSeparatorSize as i32, v)
280        },
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use super::*;
287
288    fn setup_context() -> crate::Context {
289        let mut ctx = crate::Context::create();
290        ctx.io_mut().set_display_size([128.0, 128.0]);
291        ctx.io_mut().set_delta_time(1.0 / 60.0);
292        let _ = ctx.font_atlas().build();
293        ctx
294    }
295
296    #[test]
297    fn style_var_component_scopes_are_typed_and_restore_values() {
298        let mut ctx = setup_context();
299        let ui = ctx.frame();
300        let original = ui.clone_style().window_padding();
301
302        {
303            let _x = ui.push_style_var_x(StyleVarVec2::WindowPadding, 17.0);
304            assert_eq!(ui.clone_style().window_padding(), [17.0, original[1]]);
305        }
306        assert_eq!(ui.clone_style().window_padding(), original);
307
308        {
309            let _y = ui.push_style_var_y(StyleVarVec2::WindowPadding, 23.0);
310            assert_eq!(ui.clone_style().window_padding(), [original[0], 23.0]);
311        }
312        assert_eq!(ui.clone_style().window_padding(), original);
313    }
314
315    #[test]
316    fn style_var_components_validate_values_before_ffi() {
317        let mut ctx = setup_context();
318        let ui = ctx.frame();
319
320        for value in [-1.0, f32::NAN, f32::INFINITY] {
321            assert!(
322                std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
323                    let _token = ui.push_style_var_x(StyleVarVec2::WindowPadding, value);
324                }))
325                .is_err()
326            );
327        }
328        assert!(
329            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
330                let _token = ui.push_style_var_y(StyleVarVec2::WindowTitleAlign, 1.1);
331            }))
332            .is_err()
333        );
334    }
335}