Skip to main content

dear_implot/
style.rs

1// Style and theming for plots
2
3use crate::sys;
4use dear_imgui_rs::{with_scratch_txt, with_scratch_txt_two};
5use std::os::raw::c_char;
6
7/// Style variables that can be modified
8#[repr(i32)]
9#[derive(Copy, Clone, Debug, PartialEq, Eq)]
10pub enum StyleVar {
11    PlotDefaultSize = sys::ImPlotStyleVar_PlotDefaultSize as i32,
12    PlotMinSize = sys::ImPlotStyleVar_PlotMinSize as i32,
13    PlotBorderSize = sys::ImPlotStyleVar_PlotBorderSize as i32,
14    MinorAlpha = sys::ImPlotStyleVar_MinorAlpha as i32,
15    MajorTickLen = sys::ImPlotStyleVar_MajorTickLen as i32,
16    MinorTickLen = sys::ImPlotStyleVar_MinorTickLen as i32,
17    MajorTickSize = sys::ImPlotStyleVar_MajorTickSize as i32,
18    MinorTickSize = sys::ImPlotStyleVar_MinorTickSize as i32,
19    MajorGridSize = sys::ImPlotStyleVar_MajorGridSize as i32,
20    MinorGridSize = sys::ImPlotStyleVar_MinorGridSize as i32,
21    PlotPadding = sys::ImPlotStyleVar_PlotPadding as i32,
22    LabelPadding = sys::ImPlotStyleVar_LabelPadding as i32,
23    LegendPadding = sys::ImPlotStyleVar_LegendPadding as i32,
24    LegendInnerPadding = sys::ImPlotStyleVar_LegendInnerPadding as i32,
25    LegendSpacing = sys::ImPlotStyleVar_LegendSpacing as i32,
26    MousePosPadding = sys::ImPlotStyleVar_MousePosPadding as i32,
27    AnnotationPadding = sys::ImPlotStyleVar_AnnotationPadding as i32,
28    FitPadding = sys::ImPlotStyleVar_FitPadding as i32,
29    DigitalPadding = sys::ImPlotStyleVar_DigitalPadding as i32,
30    DigitalSpacing = sys::ImPlotStyleVar_DigitalSpacing as i32,
31}
32
33/// Token for managing style variable changes
34pub struct StyleVarToken {
35    was_popped: bool,
36}
37
38impl StyleVarToken {
39    /// Pop this style variable from the stack
40    pub fn pop(mut self) {
41        if self.was_popped {
42            panic!("Attempted to pop a style var token twice.");
43        }
44        self.was_popped = true;
45        unsafe {
46            sys::ImPlot_PopStyleVar(1);
47        }
48    }
49}
50
51impl Drop for StyleVarToken {
52    fn drop(&mut self) {
53        if !self.was_popped {
54            unsafe {
55                sys::ImPlot_PopStyleVar(1);
56            }
57        }
58    }
59}
60
61/// Token for managing style color changes
62pub struct StyleColorToken {
63    was_popped: bool,
64}
65
66impl StyleColorToken {
67    /// Pop this style color from the stack
68    pub fn pop(mut self) {
69        if self.was_popped {
70            panic!("Attempted to pop a style color token twice.");
71        }
72        self.was_popped = true;
73        unsafe {
74            sys::ImPlot_PopStyleColor(1);
75        }
76    }
77}
78
79impl Drop for StyleColorToken {
80    fn drop(&mut self) {
81        if !self.was_popped {
82            unsafe {
83                sys::ImPlot_PopStyleColor(1);
84            }
85        }
86    }
87}
88
89/// Push a float style variable to the stack
90pub fn push_style_var_f32(var: StyleVar, value: f32) -> StyleVarToken {
91    unsafe {
92        sys::ImPlot_PushStyleVar_Float(var as sys::ImPlotStyleVar, value);
93    }
94    StyleVarToken { was_popped: false }
95}
96
97/// Push an integer style variable to the stack (converted to float)
98pub fn push_style_var_i32(var: StyleVar, value: i32) -> StyleVarToken {
99    unsafe {
100        sys::ImPlot_PushStyleVar_Int(var as sys::ImPlotStyleVar, value);
101    }
102    StyleVarToken { was_popped: false }
103}
104
105/// Push a Vec2 style variable to the stack
106pub fn push_style_var_vec2(var: StyleVar, value: [f32; 2]) -> StyleVarToken {
107    unsafe {
108        sys::ImPlot_PushStyleVar_Vec2(
109            var as sys::ImPlotStyleVar,
110            sys::ImVec2_c {
111                x: value[0],
112                y: value[1],
113            },
114        );
115    }
116    StyleVarToken { was_popped: false }
117}
118
119/// Push a style color to the stack
120pub fn push_style_color(element: crate::PlotColorElement, color: [f32; 4]) -> StyleColorToken {
121    unsafe {
122        // Convert color to ImU32 format (RGBA)
123        let r = (color[0] * 255.0) as u32;
124        let g = (color[1] * 255.0) as u32;
125        let b = (color[2] * 255.0) as u32;
126        let a = (color[3] * 255.0) as u32;
127        let color_u32 = (a << 24) | (b << 16) | (g << 8) | r;
128
129        sys::ImPlot_PushStyleColor_U32(element as sys::ImPlotCol, color_u32);
130    }
131    StyleColorToken { was_popped: false }
132}
133
134/// Push a colormap to the stack
135pub fn push_colormap(preset: crate::Colormap) {
136    unsafe {
137        sys::ImPlot_PushColormap_PlotColormap(preset as sys::ImPlotColormap);
138    }
139}
140
141/// Pop a colormap from the stack
142pub fn pop_colormap(count: i32) {
143    unsafe {
144        sys::ImPlot_PopColormap(count);
145    }
146}
147
148/// Add a custom colormap from a vector of colors
149pub fn add_colormap(name: &str, colors: &[sys::ImVec4], qualitative: bool) -> sys::ImPlotColormap {
150    assert!(!name.contains('\0'), "colormap name contained NUL");
151    let count = i32::try_from(colors.len()).expect("colormap contained too many colors");
152    with_scratch_txt(name, |ptr| unsafe {
153        sys::ImPlot_AddColormap_Vec4Ptr(ptr, colors.as_ptr(), count, qualitative)
154            as sys::ImPlotColormap
155    })
156}
157
158// Style editor / selectors and input-map helpers
159
160/// Show the ImPlot style editor window
161pub fn show_style_editor() {
162    unsafe { sys::ImPlot_ShowStyleEditor(std::ptr::null_mut()) }
163}
164
165/// Show the ImPlot style selector combo; returns true if selection changed
166pub fn show_style_selector(label: &str) -> bool {
167    let label = if label.contains('\0') { "" } else { label };
168    with_scratch_txt(label, |ptr| unsafe { sys::ImPlot_ShowStyleSelector(ptr) })
169}
170
171/// Show the ImPlot colormap selector combo; returns true if selection changed
172pub fn show_colormap_selector(label: &str) -> bool {
173    let label = if label.contains('\0') { "" } else { label };
174    with_scratch_txt(label, |ptr| unsafe {
175        sys::ImPlot_ShowColormapSelector(ptr)
176    })
177}
178
179/// Show the ImPlot input-map selector combo; returns true if selection changed
180pub fn show_input_map_selector(label: &str) -> bool {
181    let label = if label.contains('\0') { "" } else { label };
182    with_scratch_txt(label, |ptr| unsafe {
183        sys::ImPlot_ShowInputMapSelector(ptr)
184    })
185}
186
187/// Map input to defaults
188pub fn map_input_default() {
189    unsafe { sys::ImPlot_MapInputDefault(sys::ImPlot_GetInputMap()) }
190}
191
192/// Map input to reversed scheme
193pub fn map_input_reverse() {
194    unsafe { sys::ImPlot_MapInputReverse(sys::ImPlot_GetInputMap()) }
195}
196
197// Colormap widgets
198
199/// Draw a colormap scale widget
200pub fn colormap_scale(
201    label: &str,
202    scale_min: f64,
203    scale_max: f64,
204    height: f32,
205    cmap: Option<sys::ImPlotColormap>,
206) {
207    let label = if label.contains('\0') { "" } else { label };
208    let size = sys::ImVec2_c { x: 0.0, y: height };
209    let fmt_ptr: *const c_char = std::ptr::null();
210    let flags: i32 = 0; // ImPlotColormapScaleFlags_None
211    with_scratch_txt(label, |ptr| unsafe {
212        sys::ImPlot_ColormapScale(
213            ptr,
214            scale_min,
215            scale_max,
216            size,
217            fmt_ptr,
218            flags,
219            cmap.unwrap_or(0),
220        )
221    })
222}
223
224/// Draw a colormap slider; returns true if selection changed
225pub fn colormap_slider(
226    label: &str,
227    t: &mut f32,
228    out_color: &mut sys::ImVec4,
229    format: Option<&str>,
230    cmap: sys::ImPlotColormap,
231) -> bool {
232    let label = if label.contains('\0') { "" } else { label };
233    let format = format.filter(|s| !s.contains('\0'));
234
235    match format {
236        Some(fmt) => with_scratch_txt_two(label, fmt, |label_ptr, fmt_ptr| unsafe {
237            sys::ImPlot_ColormapSlider(
238                label_ptr,
239                t as *mut f32,
240                out_color as *mut sys::ImVec4,
241                fmt_ptr,
242                cmap,
243            )
244        }),
245        None => with_scratch_txt(label, |label_ptr| unsafe {
246            sys::ImPlot_ColormapSlider(
247                label_ptr,
248                t as *mut f32,
249                out_color as *mut sys::ImVec4,
250                std::ptr::null(),
251                cmap,
252            )
253        }),
254    }
255}
256
257/// Draw a colormap picker button; returns true if clicked
258pub fn colormap_button(label: &str, size: [f32; 2], cmap: sys::ImPlotColormap) -> bool {
259    let label = if label.contains('\0') { "" } else { label };
260    let sz = sys::ImVec2_c {
261        x: size[0],
262        y: size[1],
263    };
264    with_scratch_txt(label, |ptr| unsafe {
265        sys::ImPlot_ColormapButton(ptr, sz, cmap)
266    })
267}