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    /// `StyleVar::Alpha` participates in the same restoration order as an effective disabled
51    /// scope. Keep those two scopes lexically nested, or use a closure helper, so the saved alpha
52    /// value is restored by the operation that owns it.
53    ///
54    /// # Examples
55    ///
56    /// ```no_run
57    /// # use dear_imgui_rs::*;
58    /// # let mut ctx = Context::create();
59    /// # let ui = ctx.frame();
60    /// let style = ui.push_style_var(StyleVar::Alpha(0.2));
61    /// ui.text("I'm transparent!");
62    /// style.pop();
63    /// ```
64    #[doc(alias = "PushStyleVar")]
65    pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> {
66        validate_style_var("Ui::push_style_var()", style_var);
67        let is_alpha = matches!(style_var, StyleVar::Alpha(_));
68        self.run_with_bound_context(|| unsafe { push_style_var(style_var) });
69        StyleStackToken::new_for(self, is_alpha)
70    }
71
72    /// Overrides the X component of a two-component style variable.
73    #[doc(alias = "PushStyleVarX")]
74    pub fn push_style_var_x(&self, style_var: StyleVarVec2, value: f32) -> StyleStackToken<'_> {
75        validate_style_var_component("Ui::push_style_var_x()", style_var, value);
76        self.run_with_bound_context(|| unsafe { sys::igPushStyleVarX(style_var.raw(), value) });
77        StyleStackToken::new(self)
78    }
79
80    /// Overrides the Y component of a two-component style variable.
81    #[doc(alias = "PushStyleVarY")]
82    pub fn push_style_var_y(&self, style_var: StyleVarVec2, value: f32) -> StyleStackToken<'_> {
83        validate_style_var_component("Ui::push_style_var_y()", style_var, value);
84        self.run_with_bound_context(|| unsafe { sys::igPushStyleVarY(style_var.raw(), value) });
85        StyleStackToken::new(self)
86    }
87}
88
89create_token!(
90    /// Tracks a color pushed to the color stack that can be popped by calling `.end()`
91    /// or by dropping.
92    #[doc(alias = "PopStyleColor")]
93    pub struct ColorStackToken<'ui>;
94
95    pop crate::scope::NativeScopePop::PopStyleColor;
96
97    /// Pops a change from the color stack.
98    drop { unsafe { sys::igPopStyleColor(1) } }
99);
100
101impl ColorStackToken<'_> {
102    /// Pops a change from the color stack.
103    ///
104    /// # Panics
105    ///
106    /// Panics under the same conditions as [`Self::end`].
107    pub fn pop(self) {
108        self.end()
109    }
110}
111
112create_token!(
113    /// Tracks a style pushed to the style stack that can be popped by calling `.end()`
114    /// or by dropping.
115    #[doc(alias = "PopStyleVar")]
116    pub struct StyleStackToken<'ui>;
117
118    pop crate::scope::NativeScopePop::PopStyleVar { is_alpha: false };
119
120    /// Pops a change from the style stack.
121    drop { unsafe { sys::igPopStyleVar(1) } }
122);
123
124impl<'ui> StyleStackToken<'ui> {
125    fn new_for(ui: &'ui Ui, is_alpha: bool) -> Self {
126        Self {
127            scope: ui.begin_native_scope(
128                crate::scope::NativeScopePop::PopStyleVar { is_alpha },
129                "StyleStackToken",
130            ),
131            _phantom: std::marker::PhantomData,
132        }
133    }
134
135    /// Pops a change from the style stack.
136    ///
137    /// # Panics
138    ///
139    /// Panics under the same conditions as [`Self::end`].
140    pub fn pop(self) {
141        self.end()
142    }
143}
144
145/// Helper function to push style variables
146unsafe fn push_style_var(style_var: StyleVar) {
147    use StyleVar::*;
148    match style_var {
149        Alpha(v) => unsafe { sys::igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v) },
150        DisabledAlpha(v) => unsafe {
151            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DisabledAlpha as i32, v)
152        },
153        WindowPadding(v) => {
154            let p: [f32; 2] = v;
155            let vec = sys::ImVec2 { x: p[0], y: p[1] };
156            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, vec) }
157        }
158        WindowRounding(v) => unsafe {
159            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v)
160        },
161        WindowBorderSize(v) => unsafe {
162            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v)
163        },
164        WindowMinSize(v) => {
165            let p: [f32; 2] = v;
166            let vec = sys::ImVec2 { x: p[0], y: p[1] };
167            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, vec) }
168        }
169        WindowTitleAlign(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_WindowTitleAlign as i32, vec) }
173        }
174        ChildRounding(v) => unsafe {
175            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v)
176        },
177        ChildBorderSize(v) => unsafe {
178            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v)
179        },
180        PopupRounding(v) => unsafe {
181            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v)
182        },
183        PopupBorderSize(v) => unsafe {
184            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v)
185        },
186        FramePadding(v) => {
187            let p: [f32; 2] = v;
188            let vec = sys::ImVec2 { x: p[0], y: p[1] };
189            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, vec) }
190        }
191        FrameRounding(v) => unsafe {
192            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v)
193        },
194        FrameBorderSize(v) => unsafe {
195            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v)
196        },
197        ItemSpacing(v) => {
198            let p: [f32; 2] = v;
199            let vec = sys::ImVec2 { x: p[0], y: p[1] };
200            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, vec) }
201        }
202        ItemInnerSpacing(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_ItemInnerSpacing as i32, vec) }
206        }
207        IndentSpacing(v) => unsafe {
208            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v)
209        },
210        CellPadding(v) => {
211            let p: [f32; 2] = v;
212            let vec = sys::ImVec2 { x: p[0], y: p[1] };
213            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_CellPadding as i32, vec) }
214        }
215        ScrollbarSize(v) => unsafe {
216            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v)
217        },
218        ScrollbarRounding(v) => unsafe {
219            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v)
220        },
221        ScrollbarPadding(v) => unsafe {
222            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarPadding as i32, v)
223        },
224        GrabMinSize(v) => unsafe {
225            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v)
226        },
227        GrabRounding(v) => unsafe {
228            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v)
229        },
230        ImageRounding(v) => unsafe {
231            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ImageRounding as i32, v)
232        },
233        ImageBorderSize(v) => unsafe {
234            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_ImageBorderSize as i32, v)
235        },
236        TabRounding(v) => unsafe {
237            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v)
238        },
239        TabBorderSize(v) => unsafe {
240            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBorderSize as i32, v)
241        },
242        TabMinWidthBase(v) => unsafe {
243            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabMinWidthBase as i32, v)
244        },
245        TabMinWidthShrink(v) => unsafe {
246            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabMinWidthShrink as i32, v)
247        },
248        TabBarBorderSize(v) => unsafe {
249            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBarBorderSize as i32, v)
250        },
251        TabBarOverlineSize(v) => unsafe {
252            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TabBarOverlineSize as i32, v)
253        },
254        TableAngledHeadersAngle(v) => unsafe {
255            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TableAngledHeadersAngle as i32, v)
256        },
257        TableAngledHeadersTextAlign(v) => {
258            let p: [f32; 2] = v;
259            let vec = sys::ImVec2 { x: p[0], y: p[1] };
260            unsafe {
261                sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_TableAngledHeadersTextAlign as i32, vec)
262            }
263        }
264        TreeLinesSize(v) => unsafe {
265            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TreeLinesSize as i32, v)
266        },
267        TreeLinesRounding(v) => unsafe {
268            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_TreeLinesRounding as i32, v)
269        },
270        MenuItemRounding(v) => unsafe {
271            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_MenuItemRounding as i32, v)
272        },
273        SelectableRounding(v) => unsafe {
274            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SelectableRounding as i32, v)
275        },
276        DragDropTargetRounding(v) => unsafe {
277            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DragDropTargetRounding as i32, v)
278        },
279        ButtonTextAlign(v) => {
280            let p: [f32; 2] = v;
281            let vec = sys::ImVec2 { x: p[0], y: p[1] };
282            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, vec) }
283        }
284        SelectableTextAlign(v) => {
285            let p: [f32; 2] = v;
286            let vec = sys::ImVec2 { x: p[0], y: p[1] };
287            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, vec) }
288        }
289        SeparatorSize(v) => unsafe {
290            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SeparatorSize as i32, v)
291        },
292        SeparatorTextBorderSize(v) => unsafe {
293            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_SeparatorTextBorderSize as i32, v)
294        },
295        SeparatorTextAlign(v) => {
296            let p: [f32; 2] = v;
297            let vec = sys::ImVec2 { x: p[0], y: p[1] };
298            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SeparatorTextAlign as i32, vec) }
299        }
300        SeparatorTextPadding(v) => {
301            let p: [f32; 2] = v;
302            let vec = sys::ImVec2 { x: p[0], y: p[1] };
303            unsafe { sys::igPushStyleVar_Vec2(sys::ImGuiStyleVar_SeparatorTextPadding as i32, vec) }
304        }
305        DockingSeparatorSize(v) => unsafe {
306            sys::igPushStyleVar_Float(sys::ImGuiStyleVar_DockingSeparatorSize as i32, v)
307        },
308    }
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    fn setup_context() -> crate::Context {
316        let mut ctx = crate::Context::create();
317        ctx.io_mut().set_display_size([128.0, 128.0]);
318        ctx.io_mut().set_delta_time(1.0 / 60.0);
319        ctx.font_atlas()
320            .try_claim_legacy_renderer()
321            .expect("legacy renderer font atlas should be available")
322            .build();
323        ctx
324    }
325
326    #[test]
327    fn style_var_component_scopes_are_typed_and_restore_values() {
328        let mut ctx = setup_context();
329        let ui = ctx.frame();
330        let original = ui.clone_style().window_padding();
331
332        {
333            let _x = ui.push_style_var_x(StyleVarVec2::WindowPadding, 17.0);
334            assert_eq!(ui.clone_style().window_padding(), [17.0, original[1]]);
335        }
336        assert_eq!(ui.clone_style().window_padding(), original);
337
338        {
339            let _y = ui.push_style_var_y(StyleVarVec2::WindowPadding, 23.0);
340            assert_eq!(ui.clone_style().window_padding(), [original[0], 23.0]);
341        }
342        assert_eq!(ui.clone_style().window_padding(), original);
343    }
344
345    #[test]
346    fn style_var_components_validate_values_before_ffi() {
347        let mut ctx = setup_context();
348        let ui = ctx.frame();
349
350        for value in [-1.0, f32::NAN, f32::INFINITY] {
351            assert!(
352                std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
353                    let _token = ui.push_style_var_x(StyleVarVec2::WindowPadding, value);
354                }))
355                .is_err()
356            );
357        }
358        assert!(
359            std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
360                let _token = ui.push_style_var_y(StyleVarVec2::WindowTitleAlign, 1.1);
361            }))
362            .is_err()
363        );
364    }
365}