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