1use crate::sys;
4use dear_imgui_rs::{with_scratch_txt, with_scratch_txt_two};
5use std::borrow::Cow;
6use std::os::raw::c_char;
7
8#[repr(i32)]
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum StyleVar {
12 PlotDefaultSize = sys::ImPlotStyleVar_PlotDefaultSize as i32,
13 PlotMinSize = sys::ImPlotStyleVar_PlotMinSize as i32,
14 PlotBorderSize = sys::ImPlotStyleVar_PlotBorderSize as i32,
15 MinorAlpha = sys::ImPlotStyleVar_MinorAlpha as i32,
16 MajorTickLen = sys::ImPlotStyleVar_MajorTickLen as i32,
17 MinorTickLen = sys::ImPlotStyleVar_MinorTickLen as i32,
18 MajorTickSize = sys::ImPlotStyleVar_MajorTickSize as i32,
19 MinorTickSize = sys::ImPlotStyleVar_MinorTickSize as i32,
20 MajorGridSize = sys::ImPlotStyleVar_MajorGridSize as i32,
21 MinorGridSize = sys::ImPlotStyleVar_MinorGridSize as i32,
22 PlotPadding = sys::ImPlotStyleVar_PlotPadding as i32,
23 LabelPadding = sys::ImPlotStyleVar_LabelPadding as i32,
24 LegendPadding = sys::ImPlotStyleVar_LegendPadding as i32,
25 LegendInnerPadding = sys::ImPlotStyleVar_LegendInnerPadding as i32,
26 LegendSpacing = sys::ImPlotStyleVar_LegendSpacing as i32,
27 MousePosPadding = sys::ImPlotStyleVar_MousePosPadding as i32,
28 AnnotationPadding = sys::ImPlotStyleVar_AnnotationPadding as i32,
29 FitPadding = sys::ImPlotStyleVar_FitPadding as i32,
30 DigitalPadding = sys::ImPlotStyleVar_DigitalPadding as i32,
31 DigitalSpacing = sys::ImPlotStyleVar_DigitalSpacing as i32,
32}
33
34pub struct StyleVarToken {
36 was_popped: bool,
37}
38
39impl StyleVarToken {
40 pub fn pop(mut self) {
42 if self.was_popped {
43 panic!("Attempted to pop a style var token twice.");
44 }
45 self.was_popped = true;
46 unsafe {
47 sys::ImPlot_PopStyleVar(1);
48 }
49 }
50}
51
52impl Drop for StyleVarToken {
53 fn drop(&mut self) {
54 if !self.was_popped {
55 unsafe {
56 sys::ImPlot_PopStyleVar(1);
57 }
58 }
59 }
60}
61
62pub struct StyleColorToken {
64 was_popped: bool,
65}
66
67impl StyleColorToken {
68 pub fn pop(mut self) {
70 if self.was_popped {
71 panic!("Attempted to pop a style color token twice.");
72 }
73 self.was_popped = true;
74 unsafe {
75 sys::ImPlot_PopStyleColor(1);
76 }
77 }
78}
79
80impl Drop for StyleColorToken {
81 fn drop(&mut self) {
82 if !self.was_popped {
83 unsafe {
84 sys::ImPlot_PopStyleColor(1);
85 }
86 }
87 }
88}
89
90#[derive(Debug, Clone, Default, PartialEq)]
95pub struct PlotItemArrayStyle<'a> {
96 line_colors: Option<Cow<'a, [u32]>>,
97 fill_colors: Option<Cow<'a, [u32]>>,
98 marker_sizes: Option<Cow<'a, [f32]>>,
99 marker_line_colors: Option<Cow<'a, [u32]>>,
100 marker_fill_colors: Option<Cow<'a, [u32]>>,
101}
102
103impl<'a> PlotItemArrayStyle<'a> {
104 pub fn new() -> Self {
106 Self::default()
107 }
108
109 pub fn with_line_colors(mut self, colors: &'a [u32]) -> Self {
111 self.line_colors = Some(Cow::Borrowed(colors));
112 self
113 }
114
115 pub fn with_fill_colors(mut self, colors: &'a [u32]) -> Self {
117 self.fill_colors = Some(Cow::Borrowed(colors));
118 self
119 }
120
121 pub fn with_marker_sizes(mut self, sizes: &'a [f32]) -> Self {
123 self.marker_sizes = Some(Cow::Borrowed(sizes));
124 self
125 }
126
127 pub fn with_marker_line_colors(mut self, colors: &'a [u32]) -> Self {
129 self.marker_line_colors = Some(Cow::Borrowed(colors));
130 self
131 }
132
133 pub fn with_marker_fill_colors(mut self, colors: &'a [u32]) -> Self {
135 self.marker_fill_colors = Some(Cow::Borrowed(colors));
136 self
137 }
138
139 fn apply_to_spec(&self, spec: &mut sys::ImPlotSpec_c) {
140 spec.LineColors = self
141 .line_colors
142 .as_ref()
143 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
144 spec.FillColors = self
145 .fill_colors
146 .as_ref()
147 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
148 spec.MarkerSizes = self
149 .marker_sizes
150 .as_ref()
151 .map_or(std::ptr::null_mut(), |sizes| sizes.as_ptr() as *mut _);
152 spec.MarkerLineColors = self
153 .marker_line_colors
154 .as_ref()
155 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
156 spec.MarkerFillColors = self
157 .marker_fill_colors
158 .as_ref()
159 .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
160 }
161}
162
163pub fn with_next_plot_item_array_style<'a, R>(
168 style: PlotItemArrayStyle<'a>,
169 f: impl FnOnce() -> R,
170) -> R {
171 let previous = crate::plots::take_next_plot_spec();
172 let mut spec = previous.unwrap_or_else(crate::plots::default_plot_spec);
173 style.apply_to_spec(&mut spec);
174 crate::plots::set_next_plot_spec(Some(spec));
175
176 let out = f();
177
178 if crate::plots::take_next_plot_spec().is_some() {
179 crate::plots::set_next_plot_spec(previous);
180 }
181
182 out
183}
184
185pub fn push_style_var_f32(var: StyleVar, value: f32) -> StyleVarToken {
187 unsafe {
188 sys::ImPlot_PushStyleVar_Float(var as sys::ImPlotStyleVar, value);
189 }
190 StyleVarToken { was_popped: false }
191}
192
193pub fn push_style_var_i32(var: StyleVar, value: i32) -> StyleVarToken {
195 unsafe {
196 sys::ImPlot_PushStyleVar_Int(var as sys::ImPlotStyleVar, value);
197 }
198 StyleVarToken { was_popped: false }
199}
200
201pub fn push_style_var_vec2(var: StyleVar, value: [f32; 2]) -> StyleVarToken {
203 unsafe {
204 sys::ImPlot_PushStyleVar_Vec2(
205 var as sys::ImPlotStyleVar,
206 sys::ImVec2_c {
207 x: value[0],
208 y: value[1],
209 },
210 );
211 }
212 StyleVarToken { was_popped: false }
213}
214
215pub fn push_style_color(element: crate::PlotColorElement, color: [f32; 4]) -> StyleColorToken {
217 unsafe {
218 let r = (color[0] * 255.0) as u32;
220 let g = (color[1] * 255.0) as u32;
221 let b = (color[2] * 255.0) as u32;
222 let a = (color[3] * 255.0) as u32;
223 let color_u32 = (a << 24) | (b << 16) | (g << 8) | r;
224
225 sys::ImPlot_PushStyleColor_U32(element as sys::ImPlotCol, color_u32);
226 }
227 StyleColorToken { was_popped: false }
228}
229
230pub fn push_colormap(preset: crate::Colormap) {
232 unsafe {
233 sys::ImPlot_PushColormap_PlotColormap(preset as sys::ImPlotColormap);
234 }
235}
236
237pub fn pop_colormap(count: i32) {
239 unsafe {
240 sys::ImPlot_PopColormap(count);
241 }
242}
243
244pub fn add_colormap(name: &str, colors: &[sys::ImVec4], qualitative: bool) -> sys::ImPlotColormap {
246 assert!(!name.contains('\0'), "colormap name contained NUL");
247 let count = i32::try_from(colors.len()).expect("colormap contained too many colors");
248 with_scratch_txt(name, |ptr| unsafe {
249 sys::ImPlot_AddColormap_Vec4Ptr(ptr, colors.as_ptr(), count, qualitative)
250 as sys::ImPlotColormap
251 })
252}
253
254pub fn show_style_editor() {
258 unsafe { sys::ImPlot_ShowStyleEditor(std::ptr::null_mut()) }
259}
260
261pub fn show_style_selector(label: &str) -> bool {
263 let label = if label.contains('\0') { "" } else { label };
264 with_scratch_txt(label, |ptr| unsafe { sys::ImPlot_ShowStyleSelector(ptr) })
265}
266
267pub fn show_colormap_selector(label: &str) -> bool {
269 let label = if label.contains('\0') { "" } else { label };
270 with_scratch_txt(label, |ptr| unsafe {
271 sys::ImPlot_ShowColormapSelector(ptr)
272 })
273}
274
275pub fn show_input_map_selector(label: &str) -> bool {
277 let label = if label.contains('\0') { "" } else { label };
278 with_scratch_txt(label, |ptr| unsafe {
279 sys::ImPlot_ShowInputMapSelector(ptr)
280 })
281}
282
283pub fn map_input_default() {
285 unsafe { sys::ImPlot_MapInputDefault(sys::ImPlot_GetInputMap()) }
286}
287
288pub fn map_input_reverse() {
290 unsafe { sys::ImPlot_MapInputReverse(sys::ImPlot_GetInputMap()) }
291}
292
293pub fn colormap_scale(
297 label: &str,
298 scale_min: f64,
299 scale_max: f64,
300 height: f32,
301 cmap: Option<sys::ImPlotColormap>,
302) {
303 let label = if label.contains('\0') { "" } else { label };
304 let size = sys::ImVec2_c { x: 0.0, y: height };
305 let fmt_ptr: *const c_char = std::ptr::null();
306 let flags: i32 = 0; with_scratch_txt(label, |ptr| unsafe {
308 sys::ImPlot_ColormapScale(
309 ptr,
310 scale_min,
311 scale_max,
312 size,
313 fmt_ptr,
314 flags,
315 cmap.unwrap_or(0),
316 )
317 })
318}
319
320pub fn colormap_slider(
322 label: &str,
323 t: &mut f32,
324 out_color: &mut sys::ImVec4,
325 format: Option<&str>,
326 cmap: sys::ImPlotColormap,
327) -> bool {
328 let label = if label.contains('\0') { "" } else { label };
329 let format = format.filter(|s| !s.contains('\0'));
330
331 match format {
332 Some(fmt) => with_scratch_txt_two(label, fmt, |label_ptr, fmt_ptr| unsafe {
333 sys::ImPlot_ColormapSlider(
334 label_ptr,
335 t as *mut f32,
336 out_color as *mut sys::ImVec4,
337 fmt_ptr,
338 cmap,
339 )
340 }),
341 None => with_scratch_txt(label, |label_ptr| unsafe {
342 sys::ImPlot_ColormapSlider(
343 label_ptr,
344 t as *mut f32,
345 out_color as *mut sys::ImVec4,
346 std::ptr::null(),
347 cmap,
348 )
349 }),
350 }
351}
352
353pub fn colormap_button(label: &str, size: [f32; 2], cmap: sys::ImPlotColormap) -> bool {
355 let label = if label.contains('\0') { "" } else { label };
356 let sz = sys::ImVec2_c {
357 x: size[0],
358 y: size[1],
359 };
360 with_scratch_txt(label, |ptr| unsafe {
361 sys::ImPlot_ColormapButton(ptr, sz, cmap)
362 })
363}
364
365#[cfg(test)]
366mod tests {
367 use super::{PlotItemArrayStyle, with_next_plot_item_array_style};
368
369 #[test]
370 fn next_plot_item_array_style_is_consumed_by_next_spec() {
371 let line_colors = [0x01020304u32, 0x05060708];
372 let fill_colors = [0x11121314u32];
373 let marker_sizes = [2.0f32, 4.0, 8.0];
374
375 with_next_plot_item_array_style(
376 PlotItemArrayStyle::new()
377 .with_line_colors(&line_colors)
378 .with_fill_colors(&fill_colors)
379 .with_marker_sizes(&marker_sizes),
380 || {
381 let spec = crate::plots::plot_spec_from(7, 3, 16);
382 assert_eq!(spec.Flags, 7);
383 assert_eq!(spec.Offset, 3);
384 assert_eq!(spec.Stride, 16);
385 assert_eq!(spec.LineColors, line_colors.as_ptr() as *mut _);
386 assert_eq!(spec.FillColors, fill_colors.as_ptr() as *mut _);
387 assert_eq!(spec.MarkerSizes, marker_sizes.as_ptr() as *mut _);
388 },
389 );
390
391 let spec = crate::plots::plot_spec_from(0, 0, crate::IMPLOT_AUTO);
392 assert!(spec.LineColors.is_null());
393 assert!(spec.FillColors.is_null());
394 assert!(spec.MarkerSizes.is_null());
395 }
396
397 #[test]
398 fn next_plot_item_array_style_is_restored_if_unused() {
399 let line_colors = [0xAABBCCDDu32];
400
401 with_next_plot_item_array_style(
402 PlotItemArrayStyle::new().with_line_colors(&line_colors),
403 || {},
404 );
405
406 let spec = crate::plots::plot_spec_from(0, 0, crate::IMPLOT_AUTO);
407 assert!(spec.LineColors.is_null());
408 }
409}